Răsfoiți Sursa

Exercise result UI changes

- Change feedback message color, centralized in one function.
- "notCorrectedYet" message centralized in one function
- Add message "Temporary score: %s open question(s) not corrected yet"
  if open/oral questions in exercise
- Fix function name get_question_ribbon -> getTotalQuestionRibbon
- Rename function name (to camelCase)
- Create new function getQuestionRibbon
- Remove un used functions.
- Add new "rib-warning" class if questions is "Not reviewed yet" (oral, free)

see BT#12303
jmontoyaa 7 ani în urmă
părinte
comite
6150fe7d70

+ 24 - 20
app/Resources/public/css/base.css

@@ -92,7 +92,6 @@ a.thumbnail:hover{
 .page-course{
     padding: 2% 3%;
 }
-/*------------------------*/
 .cursor-pointer {
     cursor: pointer;
 }
@@ -127,7 +126,7 @@ a.thumbnail:hover{
     border-radius: 0;
     border: none;
 }
-header{
+header {
     padding-top: 20px;
 }
 .blackboard_show {
@@ -871,7 +870,6 @@ a.personal_agenda:hover, a.personal_agenda:hover {
 }
 
 /* Categories courses*/
-
 #course-picture {
     position:absolute;
     top:8%;
@@ -1110,7 +1108,6 @@ div.attendance-calendar-add div.row div.formw,div.attendance-calendar-edit div.r
     padding: 0px;
 }
 
-
 #message {
     margin: 0 auto;
     text-align: center;
@@ -1407,7 +1404,6 @@ div.admin_section h4 {
 .grid_title {float:left;width:30%;text-align:center}
 .grid_nav {float:left;width:35%;text-align:right}
 
-
 /* Groups boxes */
 .toolbar-groups{
     margin-bottom: 30px;
@@ -1426,7 +1422,6 @@ div.admin_section h4 {
 .group_invitation_grid_element_1 { width:100px; float:left; text-align:left;margin-bottom:5px;}
 .group_invitation_grid_element_2 { width:150px; float:left;}
 
-
 /* home group boxes */
 
 .home_group_grid_container {
@@ -1503,7 +1498,6 @@ div.admin_section h4 {
 .search_user_grid_element_3 { width:100%;  text-align:center;}
 
 /*SEARCH GROUP*/
-
 .search_group_grid_item {
     float:left;
     width:345px;
@@ -1563,7 +1557,6 @@ div.admin_section h4 {
     height:5px;
 }
 
-
 #lp_item_list .ui-state-highlight {
     height:25px;
     background: #EEEEEE;
@@ -1825,25 +1818,25 @@ div.admin_section h4 {
 
 .question_options input[type="text"] {
     padding: 6px 12px;
-    margin:2px 2px 2px 0px;
+    margin: 2px 2px 2px 0px;
     vertical-align: middle;
     display: inline-block;
     height: 34px;
     line-height: 1.42857;
     border: 1px solid #CCCCCC;
-  border-radius: 4px;
-  -moz-border-radius: 4px;
-  -webkit-border-radius: 4px;
-  color: #555555;
-  font-weight: bold;
-  background-color: #FCF8D6;
+    border-radius: 4px;
+    -moz-border-radius: 4px;
+    -webkit-border-radius: 4px;
+    color: #555555;
+    font-weight: bold;
+    background-color: #FCF8D6;
 
 }
 .question_options input[type="text"]:focus{
-   border-color:#66AFE9;
-  background-color: #ffffff;
-   outline: 0px none;
-   box-shadow:0px 1px 1px rgba(0, 0, 0, 0.075) inset, 0px 0px 8px rgba(102, 175, 233, 0.6) ;
+    border-color: #66AFE9;
+    background-color: #ffffff;
+    outline: 0px none;
+    box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.075) inset, 0px 0px 8px rgba(102, 175, 233, 0.6);
 }
 
 .exercise_description {
@@ -1993,6 +1986,13 @@ div.admin_section h4 {
     float: left;
 }
 
+.ribbon .rib-warning {
+    background: orange;
+    border-top: none;
+    border-bottom: none;
+    float: left;
+}
+
 .ribbon .rib-gray {
     background: #cccccc;
     background-image: -ms-linear-gradient(top, #bbbbbb, #cccccc);
@@ -2059,6 +2059,10 @@ div.admin_section h4 {
     border-color: #ccc transparent transparent transparent;
 }
 
+.ribbon .rib-warning::before {
+    border-color: darkorange transparent transparent transparent;
+}
+
 .ribbon .rib::before {left: 0;}
 .ribbon .rib::after {right: 0;}
 
@@ -6271,7 +6275,7 @@ ul#toolnavbox-two li a.btn{
     width: 100px;
 }
 .form-horizontal .radio{
-    padding-left: 5px;   
+    padding-left: 5px;
 }
 .checkbox input[type="checkbox"]{
     margin-left: -24px;

+ 0 - 339
main/exercise/exercise.class.php

@@ -7123,345 +7123,6 @@ class Exercise
         }
     }
 
-    /**
-     * @param int $exeId
-     * @return array
-     */
-    public function returnQuestionListByAttempt($exeId)
-    {
-        return $this->displayQuestionListByAttempt($exeId, false, true);
-    }
-
-    /**
-     * Display the exercise results
-     * @param int  $exe_id
-     * @param bool $saveUserResult save users results (true) or just show the results (false)
-     * @param bool $returnExerciseResult return array with exercise result info
-     * @return mixed
-     */
-    public function displayQuestionListByAttempt($exe_id, $saveUserResult = false, $returnExerciseResult = false)
-    {
-        global $origin, $debug;
-
-        //Getting attempt info
-        $exercise_stat_info = $this->getStatTrackExerciseInfoByExeId($exe_id);
-
-        //Getting question list
-        $question_list = array();
-        if (!empty($exercise_stat_info['data_tracking'])) {
-            $question_list = explode(',', $exercise_stat_info['data_tracking']);
-        } else {
-            //Try getting the question list only if save result is off
-            if ($saveUserResult == false) {
-                $question_list = $this->selectQuestionList();
-            }
-            error_log("Data tracking is empty! exe_id: $exe_id");
-        }
-
-        $counter = 1;
-        $total_score = 0;
-        $total_weight = 0;
-
-        $exercise_content = null;
-
-        //Hide results
-        $show_results = false;
-        $show_only_score = false;
-
-        if ($this->results_disabled == RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS) {
-            $show_results = true;
-        }
-
-        $showScoreOptions = [
-            RESULT_DISABLE_SHOW_SCORE_ONLY,
-            RESULT_DISABLE_SHOW_FINAL_SCORE_ONLY_WITH_CATEGORIES,
-            RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT
-        ];
-
-        if (in_array($this->results_disabled, $showScoreOptions)) {
-            $show_only_score = true;
-        }
-
-        if ($show_results || $show_only_score) {
-            $user_info = api_get_user_info($exercise_stat_info['exe_user_id']);
-            // Shows exercise header.
-            echo $this->show_exercise_result_header(
-                $user_info['complete_name'],
-                api_convert_and_format_date($exercise_stat_info['start_date'], DATE_TIME_FORMAT_LONG),
-                $exercise_stat_info['duration']
-            );
-        }
-
-        // Display text when test is finished #4074 and for LP #4227
-        $end_of_message = $this->selectTextWhenFinished();
-        if (!empty($end_of_message)) {
-            echo Display::return_message($end_of_message, 'normal', false);
-            echo "<div class='clear'>&nbsp;</div>";
-        }
-
-        $question_list_answers = array();
-        $media_list = array();
-        $category_list = array();
-        $tempParentId = null;
-        $mediaCounter = 0;
-        $exerciseResultInfo = array();
-
-        // Loop over all question to show results for each of them, one by one
-        if (!empty($question_list)) {
-            if ($debug) {
-                error_log('Looping question_list '.print_r($question_list, 1));
-            }
-
-            foreach ($question_list as $questionId) {
-                // Creates a temporary Question object
-                $objQuestionTmp = Question::read($questionId);
-
-                // This variable comes from exercise_submit_modal.php
-                ob_start();
-                $hotspot_delineation_result = null;
-
-                // We're inside *one* question. Go through each possible answer for this question
-                $result = $this->manageAnswers(
-                    $exercise_stat_info['exe_id'],
-                    $questionId,
-                    null,
-                    'exercise_result',
-                    array(),
-                    $saveUserResult,
-                    true,
-                    $show_results,
-                    $hotspot_delineation_result
-                );
-
-                if (empty($result)) {
-                    continue;
-                }
-
-                $total_score += $result['score'];
-                $total_weight += $result['weight'];
-
-                $question_list_answers[] = array(
-                    'question' => $result['open_question'],
-                    'answer' => $result['open_answer'],
-                    'answer_type' => $result['answer_type']
-                );
-
-                $my_total_score = $result['score'];
-                $my_total_weight = $result['weight'];
-
-                // Category report
-                $category_was_added_for_this_test = false;
-                $categoryExerciseList = $this->getListOfCategoriesWithQuestionForTest();
-
-                $category_list = array();
-                if (isset($categoryExerciseList) && !empty($categoryExerciseList)) {
-                    foreach ($categoryExerciseList as $category_id => $categoryInfo) {
-                        if (!isset($category_list[$category_id])) {
-                            $category_list[$category_id] = array();
-                            $category_list[$category_id]['score'] = 0;
-                            $category_list[$category_id]['total'] = 0;
-                        }
-                        $category_list[$category_id]['score'] += $my_total_score;
-                        $category_list[$category_id]['total'] += $my_total_weight;
-                        $category_was_added_for_this_test = true;
-                    }
-                }
-
-                // No category for this question!
-                if ($category_was_added_for_this_test == false) {
-                    if (!isset($category_list['none'])) {
-                        $category_list['none'] = array();
-                        $category_list['none']['score'] = 0;
-                        $category_list['none']['total'] = 0;
-                    }
-
-                    $category_list['none']['score'] += $my_total_score;
-                    $category_list['none']['total'] += $my_total_weight;
-                }
-
-                if ($this->selectPropagateNeg() == 0 && $my_total_score < 0) {
-                    $my_total_score = 0;
-                }
-
-                $comnt = null;
-                if ($show_results) {
-                    $comnt = get_comments($exe_id, $questionId);
-                    if (!empty($comnt)) {
-                        echo '<b>'.get_lang('Feedback').'</b>';
-                        echo '<div id="question_feedback">'.$comnt.'</div>';
-                    }
-                }
-
-                $score = array();
-                $score['result'] = get_lang('Score')." : ".
-                    ExerciseLib::show_score(
-                        $my_total_score,
-                        $my_total_weight,
-                        false,
-                        true
-                    );
-                $score['pass'] = $my_total_score >= $my_total_weight ? true : false;
-                $score['score'] = $my_total_score;
-                $score['weight'] = $my_total_weight;
-                $score['comments'] = $comnt;
-
-                $exerciseResultInfo[$questionId]['score'] = $score;
-                $exerciseResultInfo[$questionId]['details'] = $result;
-
-                // If no results we hide the results
-                if ($show_results == false) {
-                    $score = array();
-                }
-                $contents = ob_get_clean();
-
-                $question_content = '<div class="question_row">';
-
-                if ($show_results) {
-                    $show_media = false;
-                    $counterToShow = $counter;
-                    if ($objQuestionTmp->parent_id != 0) {
-
-                        if (!in_array($objQuestionTmp->parent_id, $media_list)) {
-                            $media_list[] = $objQuestionTmp->parent_id;
-                            $show_media = true;
-                        }
-                        if ($tempParentId == $objQuestionTmp->parent_id) {
-                            $mediaCounter++;
-                        } else {
-                            $mediaCounter = 0;
-                        }
-                        $counterToShow = chr(97 + $mediaCounter);
-                        $tempParentId = $objQuestionTmp->parent_id;
-                    }
-
-                    // Shows question title an description.
-                    $question_content .= $objQuestionTmp->return_header(
-                        null,
-                        $counterToShow,
-                        $score,
-                        $show_media,
-                        $this->getHideQuestionTitle()
-                    );
-
-                    // display question category, if any
-                    $question_content .= TestCategory::getCategoryNamesForQuestion(
-                        $questionId,
-                        null,
-                        true,
-                        $this->categoryMinusOne
-                    );
-                }
-                $counter++;
-                $question_content .= $contents;
-                $question_content .= '</div>';
-                $exercise_content .= $question_content;
-            } // end foreach() block that loops over all questions
-        }
-
-        $total_score_text = null;
-
-        if ($returnExerciseResult) {
-            return $exerciseResultInfo;
-        }
-
-        if ($origin != 'learnpath') {
-            if ($show_results || $show_only_score) {
-                $total_score_text .= $this->get_question_ribbon($total_score, $total_weight, true);
-            }
-        }
-
-        if (!empty($category_list) && ($show_results || $show_only_score)) {
-            //Adding total
-            $category_list['total'] = array('score' => $total_score, 'total' => $total_weight);
-            echo TestCategory::get_stats_table_by_attempt($this->id, $category_list, $this->categoryMinusOne);
-        }
-
-        echo $total_score_text;
-        echo $exercise_content;
-
-        if (!$show_only_score) {
-            echo $total_score_text;
-        }
-
-        if ($saveUserResult) {
-
-            // Tracking of results
-            $learnpath_id = $exercise_stat_info['orig_lp_id'];
-            $learnpath_item_id = $exercise_stat_info['orig_lp_item_id'];
-            $learnpath_item_view_id = $exercise_stat_info['orig_lp_item_view_id'];
-
-            if (api_is_allowed_to_session_edit()) {
-                Event::update_event_exercise(
-                    $exercise_stat_info['exe_id'],
-                    $this->selectId(),
-                    $total_score,
-                    $total_weight,
-                    api_get_session_id(),
-                    $learnpath_id,
-                    $learnpath_item_id,
-                    $learnpath_item_view_id,
-                    $exercise_stat_info['exe_duration'],
-                    '',
-                    array()
-                );
-            }
-
-            // Send notification.
-            if (!api_is_allowed_to_edit(null, true)) {
-                $isSuccess = ExerciseLib::is_success_exercise_result($total_score, $total_weight, $this->selectPassPercentage());
-                $this->sendCustomNotification($exe_id, $exerciseResultInfo, $isSuccess);
-                $this->sendNotificationForOpenQuestions($question_list_answers, $origin, $exe_id);
-                $this->sendNotificationForOralQuestions($question_list_answers, $origin, $exe_id);
-            }
-        }
-    }
-
-    /**
-     * Returns an HTML ribbon to show on top of the exercise result, with
-     * colouring depending on the success or failure of the student
-     * @param integer $score
-     * @param integer $weight
-     * @param bool $check_pass_percentage
-     * @return string
-     */
-    public function get_question_ribbon($score, $weight, $check_pass_percentage = false)
-    {
-        $eventMessage = null;
-        $ribbon = '<div class="question_row">';
-        $ribbon .= '<div class="ribbon">';
-        if ($check_pass_percentage) {
-            $is_success = ExerciseLib::is_success_exercise_result($score, $weight, $this->selectPassPercentage());
-            // Color the final test score if pass_percentage activated
-            $ribbon_total_success_or_error = '';
-            if (ExerciseLib::isPassPercentageEnabled($this->selectPassPercentage())) {
-                if ($is_success) {
-                    $eventMessage = $this->getOnSuccessMessage();
-                    $ribbon_total_success_or_error = ' ribbon-total-success';
-                } else {
-                    $eventMessage = $this->getOnFailedMessage();
-                    $ribbon_total_success_or_error = ' ribbon-total-error';
-                }
-            }
-            $ribbon .= '<div class="rib rib-total '.$ribbon_total_success_or_error.'">';
-        } else {
-            $ribbon .= '<div class="rib rib-total">';
-        }
-        $ribbon .= '<h3>'.get_lang('YourTotalScore').":&nbsp;";
-        $ribbon .= ExerciseLib::show_score($score, $weight, false, true);
-        $ribbon .= '</h3>';
-        $ribbon .= '</div>';
-
-        if ($check_pass_percentage) {
-            $ribbon .= ExerciseLib::show_success_message($score, $weight, $this->selectPassPercentage());
-        }
-        $ribbon .= '</div>';
-        $ribbon .= '</div>';
-
-        $ribbon .= $eventMessage;
-
-        return $ribbon;
-    }
-
     /**
      * Returns an array of categories details for the questions of the current
      * exercise.

+ 15 - 8
main/exercise/exercise_show.php

@@ -349,6 +349,7 @@ if (!empty($maxEditors) && count($questionList) > $maxEditors) {
     $useAdvancedEditor = false;
 }
 
+$countPendingQuestions = 0;
 foreach ($questionList as $questionId) {
     $choice = $exerciseResult[$questionId];
     // destruction of the Question object
@@ -367,7 +368,6 @@ foreach ($questionList as $questionId) {
     }
 
     $relPath = api_get_path(WEB_CODE_PATH);
-
     switch ($answerType) {
         case MULTIPLE_ANSWER_COMBINATION:
             //no break
@@ -702,7 +702,7 @@ foreach ($questionList as $questionId) {
             if (empty($comnt)) {
                 echo '<br />';
             } else {
-                echo '<div id="question_feedback">'.$comnt.'</div>';
+                echo ExerciseLib::getFeedbackText($comnt);
             }
             echo '</div>';
 
@@ -741,7 +741,7 @@ foreach ($questionList as $questionId) {
             echo '<br />';
             if (!empty($comnt)) {
                 echo '<b>'.get_lang('Feedback').'</b>';
-                echo '<div id="question_feedback">'.$comnt.'</div>';
+                echo ExerciseLib::getFeedbackText($comnt);
             }
         }
 
@@ -761,7 +761,7 @@ foreach ($questionList as $questionId) {
 
                 if ($questionScore == -1) {
                     $questionScore = 0;
-                    echo Display::return_message(get_lang('notCorrectedYet'));
+                    echo ExerciseLib::getNotCorrectedYetText();
                 }
             } else {
                 $arrmarks[] = $questionId;
@@ -822,7 +822,7 @@ foreach ($questionList as $questionId) {
 
     $score = array();
     if ($show_results) {
-        $score['result'] = get_lang('Score')." : ".ExerciseLib::show_score(
+        $score['result'] = ExerciseLib::show_score(
             $my_total_score,
             $my_total_weight,
             false,
@@ -835,11 +835,17 @@ foreach ($questionList as $questionId) {
         $score['comments'] = isset($comnt) ? $comnt : null;
     }
 
+    if (in_array($objQuestionTmp->type, [FREE_ANSWER, ORAL_EXPRESSION])) {
+        $check = $objQuestionTmp->isQuestionWaitingReview($score);
+        if ($check === false) {
+            $countPendingQuestions++;
+        }
+    }
+
     unset($objAnswerTmp);
     $i++;
 
     $contents = ob_get_clean();
-
     $question_content = '<div class="question_row">';
 
     if ($show_results) {
@@ -862,11 +868,12 @@ if ($origin != 'learnpath' || ($origin == 'learnpath' && isset($_GET['fb_type'])
         if ($objExercise->selectPropagateNeg() == 0 && $my_total_score_temp < 0) {
             $my_total_score_temp = 0;
         }
-        $total_score_text .= ExerciseLib::get_question_ribbon(
+        $total_score_text .= ExerciseLib::getTotalScoreRibbon(
             $objExercise,
             $my_total_score_temp,
             $totalWeighting,
-            true
+            true,
+            $countPendingQuestions
         );
         $total_score_text .= '</div>';
     }

+ 1 - 5
main/exercise/freeanswer.class.php

@@ -56,11 +56,7 @@ class FreeAnswer extends Question
      */
     public function return_header($feedback_type = null, $counter = null, $score = [])
     {
-        if (!empty($score['comments']) || $score['score'] > 0) {
-            $score['revised'] = true;
-        } else {
-            $score['revised'] = false;
-        }
+        $score['revised'] = $this->isQuestionWaitingReview($score);
         $header = parent::return_header($feedback_type, $counter, $score);
         $header .= '<table class="'.$this->question_table_class.'" >
         <tr>

+ 2 - 1
main/exercise/oral_expression.class.php

@@ -70,8 +70,9 @@ class OralExpression extends Question
      * @param null $score
      * @return null|string
      */
-    function return_header($feedback_type = null, $counter = null, $score = null)
+    public function return_header($feedback_type = null, $counter = null, $score = null)
     {
+        $score['revised'] = $this->isQuestionWaitingReview($score);
         $header = parent::return_header($feedback_type, $counter, $score);
         $header .= '<table class="'.$this->question_table_class.'">
             <tr>

+ 17 - 5
main/exercise/question.class.php

@@ -1911,7 +1911,7 @@ abstract class Question
                 $class = '';
             } else {
                 $score_label = get_lang('NotRevised');
-                $class = 'error';
+                $class = 'warning';
                 $weight = float_format($score['weight'], 1);
                 $score['result'] = " ? / ".$weight;
             }
@@ -1925,10 +1925,7 @@ abstract class Question
         }
 
         $header .= Display::page_subheader2($counter_label.". ".$this->question);
-        $header .= Display::div(
-            "<div class=\"rib rib-$class\"><h3>$score_label</h3></div> <h4> ".get_lang('Score').": {$score['result']}</h4>",
-            array('class' => 'ribbon')
-        );
+        $header .= ExerciseLib::getQuestionRibbon($class, $score_label, $score['result']);
         if ($this->type != READING_COMPREHENSION) {
             // Do not show the description (the text to read) if the question is of type READING_COMPREHENSION
             $header .= Display::div($this->description, array('class' => 'question_description'));
@@ -2175,4 +2172,19 @@ abstract class Question
         }
         return $swappedAnswer;
     }
+
+
+    /**
+     * @param array $score
+     * @return bool
+     */
+    public function isQuestionWaitingReview($score)
+    {
+        $isReview = false;
+        if (!empty($score['comments']) || $score['score'] > 0) {
+            $isReview = true;
+        }
+
+        return $isReview;
+    }
 }

+ 111 - 59
main/inc/lib/exercise.lib.php

@@ -114,7 +114,6 @@ class ExerciseLib
             if (in_array($answerType, [MATCHING, DRAGGABLE, MATCHING_DRAGGABLE])) {
                 if ($answerType == DRAGGABLE) {
                     $isVertical = $objQuestionTmp->extra == 'v';
-
                     $s .= '
                         <div class="col-md-12 ui-widget ui-helper-clearfix">
                             <div class="clearfix">
@@ -148,7 +147,6 @@ class ExerciseLib
                 }
 
                 $i = 1;
-
                 $select_items[0]['id'] = 0;
                 $select_items[0]['letter'] = '--';
                 $select_items[0]['answer'] = '';
@@ -239,7 +237,6 @@ class ExerciseLib
                     } else {
                         $header .= Display::tag('th', $item);
                     }
-
                 }
                 if ($show_comment) {
                     $header .= Display::tag('th', get_lang('Feedback'));
@@ -253,19 +250,17 @@ class ExerciseLib
             }
 
             if ($show_comment) {
-                if (
-                    in_array(
-                        $answerType,
-                        array(
-                            MULTIPLE_ANSWER,
-                            MULTIPLE_ANSWER_COMBINATION,
-                            UNIQUE_ANSWER,
-                            UNIQUE_ANSWER_IMAGE,
-                            UNIQUE_ANSWER_NO_OPTION,
-                            GLOBAL_MULTIPLE_ANSWER
-                        )
+                if (in_array(
+                    $answerType,
+                    array(
+                        MULTIPLE_ANSWER,
+                        MULTIPLE_ANSWER_COMBINATION,
+                        UNIQUE_ANSWER,
+                        UNIQUE_ANSWER_IMAGE,
+                        UNIQUE_ANSWER_NO_OPTION,
+                        GLOBAL_MULTIPLE_ANSWER,
                     )
-                ) {
+                )) {
                     $header = Display::tag('th', get_lang('Options'));
                     if ($exercise_feedback == EXERCISE_FEEDBACK_TYPE_END) {
                         $header .= Display::tag('th', get_lang('Feedback'));
@@ -302,7 +297,6 @@ class ExerciseLib
             }
 
             for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
-
                 $answer = $objAnswerTmp->selectAnswer($answerId);
                 $answerCorrect = $objAnswerTmp->isCorrect($answerId);
                 $numAnswer = $objAnswerTmp->selectAutoId($answerId);
@@ -908,7 +902,6 @@ class ExerciseLib
                                 if ($value == $selectedValue) {
                                     break;
                                 }
-
                                 $selectedIndex++;
                             }
 
@@ -994,7 +987,6 @@ HTML;
                                     if ($answerCorrect != $chosen['answer']) {
                                         continue;
                                     }
-
                                     $selectedValue = $chosen['answer'];
                                 }
                             }
@@ -1007,7 +999,6 @@ HTML;
                                 if ($value == $selectedValue) {
                                     break;
                                 }
-
                                 $selectedIndex++;
                             }
 
@@ -1107,7 +1098,6 @@ HTML;
                 $s .= "</ul>";
                 $s .= "</div>"; //clearfix
                 $counterAnswer = 1;
-
                 $s .= $isVertical ? '' : '<div class="row">';
 
                 for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
@@ -1313,7 +1303,6 @@ HOTSPOT;
             if (!$only_questions) {
                 if ($show_title) {
                     TestCategory::displayCategoryAndTitle($objQuestionTmp->id);
-
                     echo $objQuestionTmp->getTitleToDisplay($current_item);
                 }
                 echo '
@@ -1397,6 +1386,10 @@ HOTSPOT;
 
     /**
      * Validates the time control key
+     * @param int $exercise_id
+     * @param int $lp_id
+     * @param int $lp_item_id
+     * @return bool
      */
     public static function exercise_time_control_is_valid(
         $exercise_id,
@@ -1437,6 +1430,10 @@ HOTSPOT;
 
     /**
      * Deletes the time control token
+     *
+     * @param int $exercise_id
+     * @param int $lp_id
+     * @param int $lp_item_id
      */
     public static function exercise_time_control_delete(
         $exercise_id,
@@ -1454,8 +1451,11 @@ HOTSPOT;
     /**
      * Generates the time control key
      */
-    public static function get_time_control_key($exercise_id, $lp_id = 0, $lp_item_id = 0)
-    {
+    public static function get_time_control_key(
+        $exercise_id,
+        $lp_id = 0,
+        $lp_item_id = 0
+    ) {
         $exercise_id = intval($exercise_id);
         $lp_id = intval($lp_id);
         $lp_item_id = intval($lp_item_id);
@@ -2066,7 +2066,6 @@ HOTSPOT;
                                 }
                             }
                             $revisedLabel = '';
-
                             switch ($revised) {
                                 case 0:
                                     $actions .= "<a href='exercise_show.php?".api_get_cidreq()."&action=qualify&id=$id'>".
@@ -2234,10 +2233,8 @@ HOTSPOT;
                         null,
                         date_default_timezone_get()
                     );
-                    $hp_result = round(
-                            ($hpresults[$i][4] / ($hpresults[$i][5] != 0 ? $hpresults[$i][5] : 1)) * 100,
-                            2
-                        ).'% ('.$hpresults[$i][4].' / '.$hpresults[$i][5].')';
+                    $hp_result = round(($hpresults[$i][4] / ($hpresults[$i][5] != 0 ? $hpresults[$i][5] : 1)) * 100, 2)
+                        .'% ('.$hpresults[$i][4].' / '.$hpresults[$i][5].')';
                     if ($is_allowedToEdit) {
                         $list_info[] = array(
                             $hpresults[$i][0],
@@ -2329,7 +2326,7 @@ HOTSPOT;
      * @param string $pass_percentage
      * @return bool
      */
-    public static function is_success_exercise_result($score, $weight, $pass_percentage)
+    public static function isSuccessExerciseResult($score, $weight, $pass_percentage)
     {
         $percentage = float_format(
             ($score / ($weight != 0 ? $weight : 1)) * 100,
@@ -2349,17 +2346,17 @@ HOTSPOT;
      * @param string $pass_percentage
      * @return string
      */
-    public static function show_success_message($score, $weight, $pass_percentage)
+    public static function showSuccessMessage($score, $weight, $pass_percentage)
     {
         $res = '';
         if (self::isPassPercentageEnabled($pass_percentage)) {
-            $is_success = self::is_success_exercise_result(
+            $isSuccess = self::isSuccessExerciseResult(
                 $score,
                 $weight,
                 $pass_percentage
             );
 
-            if ($is_success) {
+            if ($isSuccess) {
                 $html = get_lang('CongratulationsYouPassedTheTest');
                 $icon = Display::return_icon(
                     'completed.png',
@@ -2463,8 +2460,7 @@ HOTSPOT;
         $search = '',
         $search_all_sessions = false,
         $active = 2
-    )
-    {
+    ) {
         $course_id = api_get_course_int_id();
 
         if (!empty($course_info) && !empty($course_info['real_id'])) {
@@ -2549,9 +2545,9 @@ HOTSPOT;
      * @param int $courseId The course ID (necessary as c_quiz.id is not unique)
      * @return array Exercise info
      */
-    public static function get_exercise_by_id($exerciseId = 0, $courseId = null)
+    public static function get_exercise_by_id($exerciseId = 0, $courseId = 0)
     {
-        $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
+        $table = Database::get_course_table(TABLE_QUIZ_TEST);
         if (empty($courseId)) {
             $courseId = api_get_course_int_id();
         } else {
@@ -2564,7 +2560,7 @@ HOTSPOT;
             )
         );
 
-        return Database::select('*', $TBL_EXERCICES, $conditions);
+        return Database::select('*', $table, $conditions);
     }
 
     /**
@@ -3758,7 +3754,6 @@ HOTSPOT;
         $question_list_answers = array();
         $media_list = array();
         $category_list = array();
-
         $loadChoiceFromSession = false;
         $fromDatabase = true;
         $exerciseResult = null;
@@ -3773,6 +3768,7 @@ HOTSPOT;
             $delineationResults = isset($delineationResults[$objExercise->id]) ? $delineationResults[$objExercise->id] : null;
         }
 
+        $countPendingQuestions = 0;
         // Loop over all question to show results for each of them, one by one
         if (!empty($question_list)) {
             foreach ($question_list as $questionId) {
@@ -3865,7 +3861,7 @@ HOTSPOT;
                     $comnt = Event::get_comments($exe_id, $questionId);
                     if (!empty($comnt)) {
                         echo '<b>'.get_lang('Feedback').'</b>';
-                        echo '<div id="question_feedback">'.$comnt.'</div>';
+                        echo ExerciseLib::getFeedbackText($comnt);
                     }
                 }
 
@@ -3886,6 +3882,13 @@ HOTSPOT;
                     $score = array();
                 }
 
+                if (in_array($objQuestionTmp->type, [FREE_ANSWER, ORAL_EXPRESSION])) {
+                    $check = $objQuestionTmp->isQuestionWaitingReview($score);
+                    if ($check === false) {
+                        $countPendingQuestions++;
+                    }
+                }
+
                 $contents = ob_get_clean();
                 $question_content = '';
                 if ($show_results) {
@@ -3910,11 +3913,12 @@ HOTSPOT;
         $total_score_text = null;
         if ($show_results || $show_only_score) {
             $total_score_text .= '<div class="question_row_score">';
-            $total_score_text .= self::get_question_ribbon(
+            $total_score_text .= self::getTotalScoreRibbon(
                 $objExercise,
                 $total_score,
                 $total_weight,
-                true
+                true,
+                $countPendingQuestions
             );
             $total_score_text .= '</div>';
         }
@@ -3995,39 +3999,57 @@ HOTSPOT;
         }
     }
 
+    /**
+     * @param string $class
+     * @param string $scoreLabel
+     * @param string $result
+     *
+     * @return string
+     */
+    public static function getQuestionRibbon($class, $scoreLabel, $result)
+    {
+        return '<div class="ribbon">
+                    <div class="rib rib-'.$class.'">
+                        <h3>'.$scoreLabel.'</h3>
+                    </div> 
+                    <h4>'.get_lang('Score').': '.$result.'</h4>
+                </div>'
+        ;
+    }
+
     /**
      * @param Exercise $objExercise
      * @param float $score
      * @param float $weight
-     * @param bool $check_pass_percentage
+     * @param bool $checkPassPercentage
+     * @param int $countPendingQuestions
      * @return string
      */
-    public static function get_question_ribbon(
+    public static function getTotalScoreRibbon(
         $objExercise,
         $score,
         $weight,
-        $check_pass_percentage = false
+        $checkPassPercentage = false,
+        $countPendingQuestions = 0
     ) {
+        $passPercentage = $objExercise->selectPassPercentage();
         $ribbon = '<div class="title-score">';
-        if ($check_pass_percentage) {
-            $is_success = self::is_success_exercise_result(
+        if ($checkPassPercentage) {
+            $isSuccess = self::isSuccessExerciseResult(
                 $score,
                 $weight,
-                $objExercise->selectPassPercentage()
+                $passPercentage
             );
             // Color the final test score if pass_percentage activated
-            $ribbon_total_success_or_error = '';
-            if (self::isPassPercentageEnabled(
-                $objExercise->selectPassPercentage()
-            )
-            ) {
-                if ($is_success) {
-                    $ribbon_total_success_or_error = ' ribbon-total-success';
+            $class = '';
+            if (self::isPassPercentageEnabled($passPercentage)) {
+                if ($isSuccess) {
+                    $class = ' ribbon-total-success';
                 } else {
-                    $ribbon_total_success_or_error = ' ribbon-total-error';
+                    $class = ' ribbon-total-error';
                 }
             }
-            $ribbon .= '<div class="total '.$ribbon_total_success_or_error.'">';
+            $ribbon .= '<div class="total '.$class.'">';
         } else {
             $ribbon .= '<div class="total">';
         }
@@ -4035,15 +4057,26 @@ HOTSPOT;
         $ribbon .= self::show_score($score, $weight, false, true);
         $ribbon .= '</h3>';
         $ribbon .= '</div>';
-        if ($check_pass_percentage) {
-            $ribbon .= self::show_success_message(
+        if ($checkPassPercentage) {
+            $ribbon .= self::showSuccessMessage(
                 $score,
                 $weight,
-                $objExercise->selectPassPercentage()
+                $passPercentage
             );
         }
         $ribbon .= '</div>';
 
+        if (!empty($countPendingQuestions)) {
+            $ribbon .= '<br />';
+            $ribbon .= Display::return_message(
+                sprintf(
+                    get_lang('TempScoreXQuestionsNotCorrectedYet'),
+                    $countPendingQuestions
+                ),
+                'warning'
+            );
+        }
+
         return $ribbon;
     }
 
@@ -4101,4 +4134,23 @@ HOTSPOT;
 
         return $message;
     }
+
+    /**
+     * @return string
+     */
+    public static function getNotCorrectedYetText()
+    {
+        return Display::return_message(get_lang('notCorrectedYet'), 'warning');
+    }
+
+    /**
+     * @param string $message
+     * @return string
+     */
+    public static function getFeedbackText($message)
+    {
+        // Old style
+        //return '<div id="question_feedback">'.$message.'</div>';
+        return Display::return_message($message, 'warning', false);
+    }
 }

+ 18 - 15
main/inc/lib/exercise_show_functions.lib.php

@@ -133,7 +133,7 @@ class ExerciseShowFunctions
             if ($questionScore > 0 || !empty($comments)) {
             } else {
                 echo '<tr>';
-                echo Display::tag('td', Display::return_message(get_lang('notCorrectedYet')), array());
+                echo Display::tag('td', ExerciseLib::getNotCorrectedYetText(), []);
                 echo '</tr>';
             }
         }
@@ -163,7 +163,7 @@ class ExerciseShowFunctions
             echo '</tr>';
             if ($feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
                 echo '<tr>';
-                echo Display::tag('td', get_lang('notCorrectedYet'), array('width'=>'45%'));
+                echo Display::tag('td', ExerciseLib::getNotCorrectedYetText(), array('width'=>'45%'));
                 echo '</tr>';
             } else {
                 echo '<tr><td>&nbsp;</td></tr>';
@@ -194,6 +194,7 @@ class ExerciseShowFunctions
      * @param string $answerComment
      * @param int $resultsDisabled
      * @param int $orderColor
+     * @param bool $showTotalScoreAndUserChoices
      */
     public static function display_hotspot_answer(
         $feedback_type,
@@ -529,8 +530,7 @@ class ExerciseShowFunctions
             } else {
                 echo $question->options[2];
             }
-        }
-        else {
+        } else {
             echo '-';
         }
         ?>
@@ -541,8 +541,8 @@ class ExerciseShowFunctions
             echo $answer;
             ?>
         </td>
-
-        <?php if ($feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) { ?>
+        <?php
+        if ($feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) { ?>
         <td width="20%">
             <?php
             //@todo replace this harcoded value
@@ -551,7 +551,6 @@ class ExerciseShowFunctions
                 if ($studentChoice == $answerCorrect) {
                     $color = "green";
                 }
-                //echo '<span style="font-weight: bold; color: #000;">'.nl2br($answerComment).'</span>';
                 if ($hide_expected_answer) {
                     $color = '';
                 }
@@ -563,14 +562,19 @@ class ExerciseShowFunctions
             if ($ans == 1) {
                 $comm = Event::get_comments($id, $questionId);
             }
-            ?>
-         <?php } else { ?>
-            <td>&nbsp;</td>
-        <?php } ?>
-        </tr>
-        <?php
+        } else {
+            echo '<td>&nbsp;</td>';
+        }
+        echo '</tr>';
     }
 
+    /**
+    * @param $feedback_type
+    * @param $exe_id
+    * @param $questionId
+    * @param null $questionScore
+    * @param int $results_disabled
+    */
     public static function displayAnnotationAnswer(
         $feedback_type,
         $exe_id,
@@ -579,10 +583,9 @@ class ExerciseShowFunctions
         $results_disabled = 0
     ) {
         $comments = Event::get_comments($exe_id, $questionId);
-
         if ($feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
             if ($questionScore <= 0 && empty($comments)) {
-                echo '<br>'.Display::return_message(get_lang('notCorrectedYet'));
+                echo '<br />'.ExerciseLib::getNotCorrectedYetText();
             }
         }
     }

+ 1 - 0
main/lang/english/trad4all.inc.php

@@ -8014,4 +8014,5 @@ $SendEmailToTeacherWhenStudentEndQuiz = "Send email to teacher when student ends
 $SendEmailToTeacherWhenStudentEndQuizOnlyIfOpenQuestion = "Send email to teacher when student ends an exercise only if an open question is answered.";
 $SendEmailToTeacherWhenStudentEndQuizOnlyIfOralQuestion = "Send email to teacher when student ends an exercise only if an oral question is answered.";
 $StudentStartExercise = "Student just started an exercise";
+$TempScoreXQuestionsNotCorrectedYet = 'Temporary score: %s open question(s) not corrected yet.';
 ?>

+ 1 - 2
main/lp/lp_view.php

@@ -303,7 +303,7 @@ if (!empty($_REQUEST['exeId']) &&
 
             if (!empty($exercise->pass_percentage)) {
                 $status = 'failed';
-                $success = ExerciseLib::is_success_exercise_result(
+                $success = ExerciseLib::isSuccessExerciseResult(
                     $score,
                     $max_score,
                     $exercise->pass_percentage
@@ -322,7 +322,6 @@ if (!empty($_REQUEST['exeId']) &&
             if ($debug) {
                 error_log($sql);
             }
-
             Database::query($sql);
 
             $sql = "UPDATE $TBL_TRACK_EXERCICES SET