Ver código fonte

Minor - update from 1.11.x

Julio Montoya 6 anos atrás
pai
commit
f5358a3e42
100 arquivos alterados com 1260 adições e 842 exclusões
  1. 1 1
      index.php
  2. 2 3
      main/admin/questions.php
  3. 1 1
      main/admin/skill_list.php
  4. 2 0
      main/auth/courses.php
  5. 2 2
      main/auth/courses_categories.php
  6. 1 1
      main/course_progress/thematic.php
  7. 1 1
      main/exercise/Draggable.php
  8. 3 0
      main/exercise/Hpdownload.php
  9. 2 2
      main/exercise/MatchingDraggable.php
  10. 3 3
      main/exercise/MultipleAnswerTrueFalseDegreeCertainty.php
  11. 4 3
      main/exercise/admin.php
  12. 2 0
      main/exercise/adminhp.php
  13. 3 0
      main/exercise/aiken.php
  14. 1 1
      main/exercise/annotation_user.php
  15. 16 19
      main/exercise/answer.class.php
  16. 81 76
      main/exercise/exercise.class.php
  17. 6 7
      main/exercise/exercise.php
  18. 2 0
      main/exercise/exercise_admin.php
  19. 32 16
      main/exercise/exercise_result.php
  20. 13 0
      main/exercise/exercise_show.php
  21. 16 16
      main/exercise/exercise_submit.php
  22. 34 6
      main/exercise/export/aiken/aiken_import.inc.php
  23. 4 8
      main/exercise/hotpotatoes.php
  24. 2 0
      main/exercise/hotspot_actionscript.as.php
  25. 1 1
      main/exercise/hotspot_admin.inc.php
  26. 1 1
      main/exercise/hotspot_answers.as.php
  27. 2 0
      main/exercise/hotspot_save.inc.php
  28. 3 0
      main/exercise/hotspot_savescore.inc.php
  29. 2 0
      main/exercise/hotspot_updatescore.inc.php
  30. 2 2
      main/exercise/matching.class.php
  31. 1 1
      main/exercise/multiple_answer.class.php
  32. 1 1
      main/exercise/multiple_answer_combination.class.php
  33. 1 1
      main/exercise/multiple_answer_true_false.class.php
  34. 2 2
      main/exercise/overview.php
  35. 2 0
      main/exercise/qti2.php
  36. 32 21
      main/exercise/question.class.php
  37. 62 61
      main/exercise/question_list_admin.inc.php
  38. 9 7
      main/exercise/question_pool.php
  39. 3 1
      main/exercise/recalculate.php
  40. 1 1
      main/exercise/result.php
  41. 3 0
      main/exercise/savescores.php
  42. 3 0
      main/exercise/showinframes.php
  43. 3 0
      main/exercise/tests_category.php
  44. 1 1
      main/exercise/unique_answer.class.php
  45. 1 1
      main/exercise/unique_answer_no_option.class.php
  46. 6 4
      main/exercise/upload_exercise.php
  47. 1 1
      main/extra/userInfo.php
  48. 1 1
      main/gamification/my_progress.php
  49. 12 5
      main/inc/lib/CoursesAndSessionsCatalog.class.php
  50. 3 4
      main/inc/lib/MoodleImport.php
  51. 40 58
      main/inc/lib/agenda.lib.php
  52. 86 0
      main/inc/lib/api.lib.php
  53. 25 25
      main/inc/lib/course.lib.php
  54. 3 3
      main/inc/lib/events.lib.php
  55. 53 6
      main/inc/lib/exercise.lib.php
  56. 17 8
      main/inc/lib/extra_field.lib.php
  57. 2 4
      main/inc/lib/formvalidator/FormValidator.class.php
  58. 126 86
      main/inc/lib/myspace.lib.php
  59. 26 31
      main/inc/lib/sessionmanager.lib.php
  60. 113 36
      main/inc/lib/social.lib.php
  61. 7 3
      main/inc/lib/sortable_table.class.php
  62. 0 3
      main/inc/lib/system_announcements.lib.php
  63. 9 0
      main/inc/lib/text.lib.php
  64. 3 2
      main/inc/lib/timeline.lib.php
  65. 42 48
      main/inc/lib/tracking.lib.php
  66. 20 10
      main/inc/lib/usermanager.lib.php
  67. 16 28
      main/inc/lib/userportal.lib.php
  68. 11 9
      main/link/link_goto.php
  69. 6 1
      main/messages/download.php
  70. 10 1
      main/messages/inbox.php
  71. 13 5
      main/messages/new_message.php
  72. 10 2
      main/messages/outbox.php
  73. 7 14
      main/mySpace/index.php
  74. 10 5
      main/mySpace/myStudents.php
  75. 7 5
      main/search/search_suggestions.php
  76. 1 1
      main/session/about.php
  77. 4 1
      main/session/add_edit_users_to_session.php
  78. 1 1
      main/session/add_teachers_to_session.php
  79. 1 1
      main/session/add_users_to_session.php
  80. 1 1
      main/session/add_users_to_session_course.php
  81. 40 31
      main/session/index.php
  82. 15 9
      main/session/resume_session.php
  83. 92 38
      main/session/session_category_list.php
  84. 1 1
      main/session/session_course_edit.php
  85. 1 1
      main/session/session_course_list.php
  86. 2 3
      main/session/session_course_user.php
  87. 3 3
      main/session/session_course_user_list.php
  88. 1 0
      main/session/session_export.php
  89. 16 3
      main/session/session_list.php
  90. 1 3
      main/social/friends.php
  91. 2 2
      main/social/group_add.php
  92. 1 1
      main/social/group_edit.php
  93. 3 3
      main/social/group_invitation.php
  94. 2 2
      main/social/group_members.php
  95. 2 2
      main/social/group_topics.php
  96. 1 0
      main/social/group_view.php
  97. 2 2
      main/social/group_waiting_list.php
  98. 4 0
      main/social/groups.php
  99. 1 49
      main/social/home.php
  100. 7 8
      main/social/invitations.php

+ 1 - 1
index.php

@@ -36,7 +36,7 @@ $controller = new IndexManager($header_title);
 $loginFailed = isset($_GET['loginFailed']) ? true : isset($loginFailed);
 if (!empty($_GET['logout'])) {
     $redirect = !empty($_GET['no_redirect']) ? false : true;
-    // pass $logoutInfo defined in local.inc.php
+    // pass f defined in local.inc.php
     $controller->logout($redirect, $logoutInfo);
 }
 /**

+ 2 - 3
main/admin/questions.php

@@ -95,9 +95,8 @@ if ($formSent) {
             $courseCode = $courseInfo['code'];
             $question->courseCode = $courseCode;
             // Creating empty exercise
-            $exercise = new Exercise();
-            $exercise->course_id = $courseId;
-            $questionObject = Question::read($question->getId(), $courseId);
+            $exercise = new Exercise($courseId);
+            $questionObject = Question::read($question->getId(), $courseInfo);
 
             ob_start();
             ExerciseLib::showQuestion(

+ 1 - 1
main/admin/skill_list.php

@@ -189,7 +189,7 @@ switch ($action) {
         $tpl->assign('skills', $skillList);
         $tpl->assign('current_tag_id', $extraFieldSearchTagId);
         $tpl->assign('tags', $tags);
-        $templateName = $tpl->get_template('skill/list.html.twig');
+        $templateName = $tpl->get_template('skill/list.tpl');
         $content = $tpl->fetch($templateName);
 
         $tpl->assign(

+ 2 - 0
main/auth/courses.php

@@ -179,6 +179,8 @@ switch ($action) {
                 exit;
             }
         }
+
+        $courseController->courseList($action);
         break;
     case 'subscribe_course':
         if (api_is_anonymous()) {

+ 2 - 2
main/auth/courses_categories.php

@@ -38,8 +38,8 @@ $select = null;
 $message = null;
 ?>
 <script>
-    $(document).ready( function() {
-        $('#stars li').on('click', function(event) {
+    $(function() {
+        $('.star-rating li a').on('click', function(event) {
             var id = $(this).parents('ul').attr('id');
             $('#vote_label2_' + id).html("<?php echo get_lang('Loading'); ?>");
             $.ajax({

+ 1 - 1
main/course_progress/thematic.php

@@ -295,7 +295,7 @@ if ($action == 'thematic_list') {
 $tpl->assign('actions', $toolbar);
 if (!empty($html)) {
     $tpl->assign('content', $html);
-    $thematicLayout = $tpl->get_template('course_progress/layout.html.twig');
+    $thematicLayout = $tpl->get_template('course_progress/layout.tpl');
 }
 if (!empty($message) && !empty($total_average_of_advances)) {
     $tpl->assign('message', $message);

+ 1 - 1
main/exercise/Draggable.php

@@ -93,7 +93,7 @@ class Draggable extends Question
         );
 
         // DISPLAY MATCHES
-        $html = '<table class="table table-hover">
+        $html = '<table class="table table-striped table-hover">
             <thead>
                 <tr>
                     <th width="85%">'.get_lang('Answer').'</th>

+ 3 - 0
main/exercise/Hpdownload.php

@@ -12,6 +12,9 @@
 session_cache_limiter('public');
 
 require_once __DIR__.'/../inc/global.inc.php';
+
+api_protect_course_script(true);
+
 $this_section = SECTION_COURSES;
 
 $tbl_document = Database::get_course_table(TABLE_DOCUMENT);

+ 2 - 2
main/exercise/MatchingDraggable.php

@@ -96,7 +96,7 @@ class MatchingDraggable extends Question
         $form->addElement('hidden', 'nb_options', $nb_options);
 
         // DISPLAY MATCHES
-        $html = '<table class="table table-hover">
+        $html = '<table class="table table-striped table-hover">
             <thead>
                 <tr>
                     <th width="10">'.get_lang('Number').'</th>
@@ -158,7 +158,7 @@ class MatchingDraggable extends Question
         $form->addHtml('</tbody></table>');
 
         // DISPLAY OPTIONS
-        $html = '<table class="table table-hover">
+        $html = '<table class="table table-striped table-hover">
             <thead>
                 <tr>
                     <th width="15%">'.get_lang('Number').'</th>

+ 3 - 3
main/exercise/MultipleAnswerTrueFalseDegreeCertainty.php

@@ -130,18 +130,18 @@ class MultipleAnswerTrueFalseDegreeCertainty extends Question
             $defaults['correct['.$i.']'] = '';
 
             if (is_object($answer)) {
-                $defaults['answer['.$i.']'] = $answer->answer[$i] ?? '';
+                $defaults['answer['.$i.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : '';
                 if (isset($_POST['answer']) && isset($_POST['answer'][$i])) {
                     $defaults['answer['.$i.']'] = Security::remove_XSS($_POST['answer'][$i]);
                 }
 
-                $defaults['comment['.$i.']'] = $answer->comment[$i] ?? '';
+                $defaults['comment['.$i.']'] = isset($answer->comment[$i]) ? $answer->comment[$i] : '';
                 if (isset($_POST['comment']) && isset($_POST['comment'][$i])) {
                     $defaults['comment['.$i.']'] = Security::remove_XSS($_POST['comment'][$i]);
                 }
 
                 $defaults['weighting['.$i.']'] = isset($answer->weighting[$i]) ? float_format($answer->weighting[$i], 1) : '';
-                $correct = $answer->correct[$i] ?? '';
+                $correct = isset($answer->correct[$i]) ? $answer->correct[$i] : '';
                 $defaults['correct['.$i.']'] = $correct;
                 if (isset($_POST['correct']) && isset($_POST['correct'][$i])) {
                     $defaults['correct['.$i.']'] = Security::remove_XSS($_POST['correct'][$i]);

+ 4 - 3
main/exercise/admin.php

@@ -223,7 +223,7 @@ if ($cancelQuestion) {
     // if we are creating a new question from the question pool
     if (!$exerciseId && !$questionId) {
         // goes back to the question pool
-        header('Location: question_pool.php');
+        header('Location: question_pool.php?'.api_get_cidreq());
         exit();
     } else {
         // goes back to the question viewing
@@ -263,7 +263,8 @@ if ($cancelAnswers) {
     $editQuestion = $modifyAnswers;
     unset($modifyAnswers);
 }
-$nameTools = null;
+
+$nameTools = '';
 // modifies the query string that is used in the link of tool name
 if ($editQuestion || $modifyQuestion || $newQuestion || $modifyAnswers) {
     $nameTools = get_lang('QuestionManagement');
@@ -308,7 +309,7 @@ $htmlHeadXtra[] = api_get_js('jqueryui-touch-punch/jquery.ui.touch-punch.min.js'
 $htmlHeadXtra[] = api_get_js('jquery.jsPlumb.all.js');
 
 $template = new Template();
-$templateName = $template->get_template('exercise/submit.js.html.twig');
+$templateName = $template->get_template('exercise/submit.js.tpl');
 $htmlHeadXtra[] = $template->fetch($templateName);
 $htmlHeadXtra[] = api_get_js('d3/jquery.xcolor.js');
 $htmlHeadXtra[] = '<link rel="stylesheet" href="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/css/hotspot.css">';

+ 2 - 0
main/exercise/adminhp.php

@@ -12,6 +12,8 @@ require_once __DIR__.'/../inc/global.inc.php';
 
 $this_section = SECTION_COURSES;
 
+api_protect_course_script(true);
+
 $_course = api_get_course_info();
 
 if (isset($_REQUEST["cancel"])) {

+ 3 - 0
main/exercise/aiken.php

@@ -10,6 +10,9 @@
  * @author César Perales <cesar.perales@gmail.com> Updated function names and import files for Aiken format support
  */
 require_once __DIR__.'/../inc/global.inc.php';
+
+api_protect_course_script(true);
+
 $lib_path = api_get_path(LIBRARY_PATH);
 $main_path = api_get_path(SYS_CODE_PATH);
 

+ 1 - 1
main/exercise/annotation_user.php

@@ -14,7 +14,7 @@ if (empty($courseInfo)) {
     return '';
 }
 
-$objQuestion = Question::read($questionId, $courseId);
+$objQuestion = Question::read($questionId, $courseInfo);
 $documentPath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
 $picturePath = $documentPath.'/images';
 $pictureSize = getimagesize($picturePath.'/'.$objQuestion->getPictureFilename());

+ 16 - 19
main/exercise/answer.class.php

@@ -44,6 +44,8 @@ class Answer
     public $questionJSId;
     public $standalone;
 
+    private $exercise;
+
     /**
      * constructor of the class.
      *
@@ -52,8 +54,9 @@ class Answer
      * @param int      $questionId that answers belong to
      * @param int      $course_id
      * @param Exercise $exercise
+     * @param bool     $readAnswer
      */
-    public function __construct($questionId, $course_id = 0, $exercise = null)
+    public function __construct($questionId, $course_id = 0, $exercise = null, $readAnswer = true)
     {
         $this->questionId = (int) $questionId;
         $this->answer = [];
@@ -84,11 +87,14 @@ class Answer
         } else {
             $objExercise = $exercise;
         }
+        $this->exercise = $objExercise;
 
-        if ($objExercise->random_answers == '1' && $this->getQuestionType() != CALCULATED_ANSWER) {
-            $this->readOrderedBy('rand()', ''); // randomize answers
-        } else {
-            $this->read(); // natural order
+        if ($readAnswer) {
+            if ($objExercise->random_answers == '1' && $this->getQuestionType() != CALCULATED_ANSWER) {
+                $this->readOrderedBy('rand()', ''); // randomize answers
+            } else {
+                $this->read(); // natural order
+            }
         }
     }
 
@@ -670,7 +676,6 @@ class Answer
         $questionId = (int) $this->questionId;
 
         $courseId = $this->course['real_id'];
-        $correctList = [];
         $answerList = [];
 
         for ($i = 1; $i <= $this->new_nbrAnswers; $i++) {
@@ -710,16 +715,13 @@ class Answer
                         ->setId($iid)
                         ->setIdAuto($iid);
 
-                    $em->merge($quizAnswer);
-                    $em->flush();
-
                     $questionType = $this->getQuestionType();
 
                     if (in_array(
                         $questionType,
                         [MATCHING, MATCHING_DRAGGABLE]
                     )) {
-                        $answer = new Answer($this->questionId);
+                        $answer = new Answer($this->questionId, $courseId, $this->exercise, false);
                         $answer->read();
                         $correctAnswerId = $answer->selectAnswerIdByPosition($correct);
 
@@ -728,13 +730,12 @@ class Answer
                         if ($questionType == MATCHING && !$correctAnswerId) {
                             continue;
                         }
-
                         $correctAnswerAutoId = $answer->selectAutoId($correct);
                         $quizAnswer->setCorrect($correctAnswerAutoId ? $correctAnswerAutoId : 0);
-
-                        $em->merge($quizAnswer);
-                        $em->flush();
                     }
+
+                    $em->merge($quizAnswer);
+                    $em->flush();
                 }
             } else {
                 // https://support.chamilo.org/issues/6558
@@ -754,13 +755,9 @@ class Answer
             }
 
             $answerList[$i] = $iid;
-
-            if ($correct) {
-                $correctList[$iid] = true;
-            }
         }
 
-        $questionType = self::getQuestionType();
+        $questionType = $this->getQuestionType();
 
         switch ($questionType) {
             case MATCHING_DRAGGABLE:

+ 81 - 76
main/exercise/exercise.class.php

@@ -773,6 +773,10 @@ class Exercise
      */
     public function getQuestionOrderedListByName()
     {
+        if (empty($this->course_id) || empty($this->id)) {
+            return [];
+        }
+
         $exerciseQuestionTable = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
         $questionTable = Database::get_course_table(TABLE_QUIZ_QUESTION);
 
@@ -3307,7 +3311,7 @@ class Exercise
 
         // Creates a temporary Question object
         $course_id = $this->course_id;
-        $objQuestionTmp = Question::read($questionId, $course_id);
+        $objQuestionTmp = Question::read($questionId, $this->course);
 
         if ($objQuestionTmp === false) {
             return false;
@@ -5178,7 +5182,7 @@ class Exercise
                                         }
                                         //type no error shouldn't be treated
                                         $next = 1;
-                                        continue;
+                                        break;
                                     }
                                     if ($debug > 0) {
                                         error_log(__LINE__.' - answerId is >1 so we\'re probably in OAR', 0);
@@ -5192,7 +5196,7 @@ class Exercise
                                     if ($overlap == false) {
                                         //all good, no overlap
                                         $next = 1;
-                                        continue;
+                                        break;
                                     } else {
                                         if ($debug > 0) {
                                             error_log(__LINE__.' - Overlap is '.$overlap.': OAR hit', 0);
@@ -6442,7 +6446,6 @@ class Exercise
         $new_question_list = [];
         if (!empty($question_list)) {
             $media_questions = $this->getMediaList();
-
             $media_active = $this->mediaIsActivated($media_questions);
 
             if ($media_active) {
@@ -6488,66 +6491,66 @@ class Exercise
      */
     public function get_validated_question_list()
     {
-        $result = [];
         $isRandomByCategory = $this->isRandomByCat();
         if ($isRandomByCategory == 0) {
             if ($this->isRandom()) {
-                $result = $this->getRandomList();
-            } else {
-                $result = $this->selectQuestionList();
-            }
-        } else {
-            if ($this->isRandom()) {
-                // USE question categories
-                // get questions by category for this exercise
-                // we have to choice $objExercise->random question in each array values of $tabCategoryQuestions
-                // key of $tabCategoryQuestions are the categopy id (0 for not in a category)
-                // value is the array of question id of this category
-                $questionList = [];
-                $tabCategoryQuestions = TestCategory::getQuestionsByCat($this->id);
-                $isRandomByCategory = $this->getRandomByCategory();
-                // We sort categories based on the term between [] in the head
-                // of the category's description
-                /* examples of categories :
-                 * [biologie] Maitriser les mecanismes de base de la genetique
-                 * [biologie] Relier les moyens de depenses et les agents infectieux
-                 * [biologie] Savoir ou est produite l'enrgie dans les cellules et sous quelle forme
-                 * [chimie] Classer les molles suivant leur pouvoir oxydant ou reacteur
-                 * [chimie] Connaître la denition de la theoie acide/base selon Brönsted
-                 * [chimie] Connaître les charges des particules
-                 * We want that in the order of the groups defined by the term
-                 * between brackets at the beginning of the category title
-                */
-                // If test option is Grouped By Categories
-                if ($isRandomByCategory == 2) {
-                    $tabCategoryQuestions = TestCategory::sortTabByBracketLabel($tabCategoryQuestions);
+                return $this->getRandomList();
+            }
+
+            return $this->selectQuestionList();
+        }
+
+        if ($this->isRandom()) {
+            // USE question categories
+            // get questions by category for this exercise
+            // we have to choice $objExercise->random question in each array values of $tabCategoryQuestions
+            // key of $tabCategoryQuestions are the categopy id (0 for not in a category)
+            // value is the array of question id of this category
+            $questionList = [];
+            $tabCategoryQuestions = TestCategory::getQuestionsByCat($this->id);
+            $isRandomByCategory = $this->getRandomByCategory();
+            // We sort categories based on the term between [] in the head
+            // of the category's description
+            /* examples of categories :
+             * [biologie] Maitriser les mecanismes de base de la genetique
+             * [biologie] Relier les moyens de depenses et les agents infectieux
+             * [biologie] Savoir ou est produite l'enrgie dans les cellules et sous quelle forme
+             * [chimie] Classer les molles suivant leur pouvoir oxydant ou reacteur
+             * [chimie] Connaître la denition de la theoie acide/base selon Brönsted
+             * [chimie] Connaître les charges des particules
+             * We want that in the order of the groups defined by the term
+             * between brackets at the beginning of the category title
+            */
+            // If test option is Grouped By Categories
+            if ($isRandomByCategory == 2) {
+                $tabCategoryQuestions = TestCategory::sortTabByBracketLabel($tabCategoryQuestions);
+            }
+            foreach ($tabCategoryQuestions as $tabquestion) {
+                $number_of_random_question = $this->random;
+                if ($this->random == -1) {
+                    $number_of_random_question = count($this->questionList);
                 }
-                foreach ($tabCategoryQuestions as $tabquestion) {
-                    $number_of_random_question = $this->random;
-                    if ($this->random == -1) {
-                        $number_of_random_question = count($this->questionList);
-                    }
-                    $questionList = array_merge(
-                        $questionList,
-                        TestCategory::getNElementsFromArray(
-                            $tabquestion,
-                            $number_of_random_question
-                        )
-                    );
-                }
-                // shuffle the question list if test is not grouped by categories
-                if ($isRandomByCategory == 1) {
-                    shuffle($questionList); // or not
-                }
-                $result = $questionList;
-            } else {
-                // Problem, random by category has been selected and
-                // we have no $this->isRandom number of question selected
-                // Should not happened
+                $questionList = array_merge(
+                    $questionList,
+                    TestCategory::getNElementsFromArray(
+                        $tabquestion,
+                        $number_of_random_question
+                    )
+                );
             }
+            // shuffle the question list if test is not grouped by categories
+            if ($isRandomByCategory == 1) {
+                shuffle($questionList); // or not
+            }
+            // Problem, random by category has been selected and
+            return $questionList;
         }
 
-        return $result;
+        // Problem, random by category has been selected and
+        // we have no $this->isRandom number of question selected
+        // Should not happened
+
+        return [];
     }
 
     public function get_question_list($expand_media_questions = false)
@@ -7465,7 +7468,7 @@ class Exercise
         } else {
             // standard test, just add each question score
             foreach ($questionList as $questionId) {
-                $question = Question::read($questionId, $this->course_id);
+                $question = Question::read($questionId, $this->course);
                 $out_max_score += $question->weighting;
             }
         }
@@ -7906,35 +7909,35 @@ class Exercise
 
     /**
      * @param int $start
-     * @param int $lenght
+     * @param int $length
      *
      * @return array
      */
-    public function getQuestionForTeacher($start = 0, $lenght = 10)
+    public function getQuestionForTeacher($start = 0, $length = 10)
     {
         $start = (int) $start;
         if ($start < 0) {
             $start = 0;
         }
 
-        $lenght = (int) $lenght;
+        $length = (int) $length;
 
-        $TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
-        $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
-        $sql = "SELECT DISTINCT e.question_id, e.question_order
-                FROM $TBL_EXERCICE_QUESTION e
-                INNER JOIN $TBL_QUESTIONS q
-                ON (e.question_id = q.id AND e.c_id = q.c_id)
+        $quizRelQuestion = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
+        $question = Database::get_course_table(TABLE_QUIZ_QUESTION);
+        $sql = "SELECT DISTINCT e.question_id
+                FROM $quizRelQuestion e
+                INNER JOIN $question q
+                ON (e.question_id = q.iid AND e.c_id = q.c_id)
                 WHERE
                     e.c_id = {$this->course_id} AND
                     e.exercice_id = '".$this->id."'
                 ORDER BY question_order
-                LIMIT $start, $lenght
+                LIMIT $start, $length
             ";
         $result = Database::query($sql);
         $questionList = [];
         while ($object = Database::fetch_object($result)) {
-            $questionList[$object->question_order] = $object->question_id;
+            $questionList[] = $object->question_id;
         }
 
         return $questionList;
@@ -8087,13 +8090,12 @@ class Exercise
      */
     private function getQuestionOrderedList()
     {
-        $questionList = [];
         $TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
         $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
 
         // Getting question_order to verify that the question
         // list is correct and all question_order's were set
-        $sql = "SELECT DISTINCT e.question_order
+        $sql = "SELECT DISTINCT count(e.question_order) as count
                 FROM $TBL_EXERCICE_QUESTION e
                 INNER JOIN $TBL_QUESTIONS q
                 ON (e.question_id = q.id AND e.c_id = q.c_id)
@@ -8102,7 +8104,8 @@ class Exercise
                   e.exercice_id	= ".$this->id;
 
         $result = Database::query($sql);
-        $count_question_orders = Database::num_rows($result);
+        $row = Database::fetch_array($result);
+        $count_question_orders = $row['count'];
 
         // Getting question list from the order (question list drag n drop interface).
         $sql = "SELECT DISTINCT e.question_id, e.question_order
@@ -8119,6 +8122,7 @@ class Exercise
         // the key of the array is the question position
         $temp_question_list = [];
         $counter = 1;
+        $questionList = [];
         while ($new_object = Database::fetch_object($result)) {
             // Correct order.
             $questionList[$new_object->question_order] = $new_object->question_id;
@@ -8389,7 +8393,7 @@ class Exercise
             $msg = str_replace("#mail#", $user_info['email'], $msg1);
             $msg = str_replace("#course#", $courseInfo['name'], $msg1);
 
-            if ($origin != 'learnpath') {
+            if (!in_array($origin, ['learnpath', 'embeddable'])) {
                 $msg .= '<br /><a href="#url#">'.get_lang('ClickToCommentAndGiveFeedback').'</a>';
             }
             $msg1 = str_replace("#url#", $url_email, $msg);
@@ -8429,6 +8433,8 @@ class Exercise
     private function setMediaList($questionList)
     {
         $mediaList = [];
+        /*
+         * Media feature is not activated in 1.11.x
         if (!empty($questionList)) {
             foreach ($questionList as $questionId) {
                 $objQuestionTmp = Question::read($questionId, $this->course_id);
@@ -8436,13 +8442,12 @@ class Exercise
                 if (isset($objQuestionTmp->parent_id) && $objQuestionTmp->parent_id != 0) {
                     $mediaList[$objQuestionTmp->parent_id][] = $objQuestionTmp->id;
                 } else {
-                    if ($objQuestionTmp) {
                         // Always the last item
                         $mediaList[999][] = $objQuestionTmp->id;
                     }
                 }
-            }
-        }
+        }*/
+
         $this->mediaList = $mediaList;
     }
 

+ 6 - 7
main/exercise/exercise.php

@@ -974,7 +974,7 @@ if (!empty($exerciseList)) {
 
                     // Check if this exercise was added in a LP
                     if ($exercise->exercise_was_added_in_lp == true) {
-                        $visiblity = Display::return_icon(
+                        $visibility = Display::return_icon(
                             'invisible.png',
                             get_lang('AddedToLPCannotBeAccessed'),
                             '',
@@ -982,7 +982,7 @@ if (!empty($exerciseList)) {
                         );
                     } else {
                         if ($row['active'] == 0 || $visibility == 0) {
-                            $visiblity = Display::url(
+                            $visibility = Display::url(
                                 Display::return_icon(
                                     'invisible.png',
                                     get_lang('Activate'),
@@ -993,7 +993,7 @@ if (!empty($exerciseList)) {
                             );
                         } else {
                             // else if not active
-                            $visiblity = Display::url(
+                            $visibility = Display::url(
                                 Display::return_icon(
                                     'visible.png',
                                     get_lang('Deactivate'),
@@ -1006,10 +1006,10 @@ if (!empty($exerciseList)) {
                     }
 
                     if ($limitTeacherAccess && !api_is_platform_admin()) {
-                        $visiblity = '';
+                        $visibility = '';
                     }
 
-                    $actions .= $visiblity;
+                    $actions .= $visibility;
 
                     $actions .= '<a href="exercise_report.php?'.api_get_cidreq().'&exerciseId='.$row['id'].'">'.
                         Display::return_icon('test_results.png', get_lang('Results'), '', ICON_SIZE_SMALL).'</a>';
@@ -1076,7 +1076,7 @@ if (!empty($exerciseList)) {
                         $number_of_questions .= min(TestCategory::getNumberMaxQuestionByCat($my_exercise_id), $random_number_of_question).' '.get_lang('QuestionByCategory');
                     } else {
                         $random_label = ' ('.get_lang('Random').') ';
-                        $number_of_questions = $random_number_of_question.' '.$random_label;
+                        $number_of_questions = $random_number_of_question.' '.$random_label.' / '.$rowi;
                         //Bug if we set a random value bigger than the real number of questions
                         if ($random_number_of_question > $rowi) {
                             $number_of_questions = $rowi.' '.$random_label;
@@ -1167,7 +1167,6 @@ if (!empty($exerciseList)) {
                                 $attempt_text = get_lang('NotAttempted');
                             }
                         } else {
-                            //$attempt_text = get_lang('CantShowResults');
                             $attempt_text = '-';
                         }
                     } else {

+ 2 - 0
main/exercise/exercise_admin.php

@@ -15,6 +15,8 @@ use ChamiloSession as Session;
 require_once __DIR__.'/../inc/global.inc.php';
 $this_section = SECTION_COURSES;
 
+api_protect_course_script(true);
+
 if (!api_is_allowed_to_edit(null, true)) {
     api_not_allowed(true);
 }

+ 32 - 16
main/exercise/exercise_result.php

@@ -75,7 +75,7 @@ if (api_get_configuration_value('quiz_prevent_copy_paste')) {
     $htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'jquery.nocopypaste.js"></script>';
 }
 
-if ($origin != 'learnpath') {
+if (!in_array($origin, ['learnpath', 'embeddable'])) {
     // So we are not in learnpath tool
     Display::display_header($nameTools, get_lang('Exercise'));
 } else {
@@ -88,7 +88,7 @@ if ($origin != 'learnpath') {
 }
 
 // I'm in a preview mode as course admin. Display the action menu.
-if (api_is_course_admin() && $origin != 'learnpath') {
+if (api_is_course_admin() && !in_array($origin, ['learnpath', 'embeddable'])) {
     echo '<div class="actions">';
     echo '<a href="admin.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id.'">'.
         Display::return_icon('back.png', get_lang('GoBackToQuestionList'), [], 32).'</a>';
@@ -121,17 +121,21 @@ if ($origin == 'learnpath') {
 $i = $total_score = $max_score = 0;
 
 $remainingMessage = '';
-$attemptButton = Display::toolbarButton(
-    get_lang('AnotherAttempt'),
-    api_get_path(WEB_CODE_PATH).'exercise/overview.php?'.api_get_cidreq().'&'.http_build_query([
-        'exerciseId' => $objExercise->id,
-        'learnpath_id' => $learnpath_id,
-        'learnpath_item_id' => $learnpath_item_id,
-        'learnpath_item_view_id' => $learnpath_item_view_id,
-    ]),
-    'pencil-square-o',
-    'info'
-);
+$attemptButton = '';
+
+if ($origin !== 'embeddable') {
+    $attemptButton = Display::toolbarButton(
+        get_lang('AnotherAttempt'),
+        api_get_path(WEB_CODE_PATH).'exercise/overview.php?'.api_get_cidreq().'&'.http_build_query([
+            'exerciseId' => $objExercise->id,
+            'learnpath_id' => $learnpath_id,
+            'learnpath_item_id' => $learnpath_item_id,
+            'learnpath_item_view_id' => $learnpath_item_view_id,
+        ]),
+        'pencil-square-o',
+        'info'
+    );
+}
 
 // We check if the user attempts before sending to the exercise_result.php
 if ($objExercise->selectAttempts() > 0) {
@@ -148,7 +152,7 @@ if ($objExercise->selectAttempts() > 0) {
             'warning',
             false
         );
-        if ($origin != 'learnpath') {
+        if (!in_array($origin, ['learnpath', 'embeddable'])) {
             //we are not in learnpath tool
             Display::display_footer();
         }
@@ -163,7 +167,7 @@ if ($objExercise->selectAttempts() > 0) {
         }
     }
 } else {
-    $remainingMessage = "<p>$attemptButton</p>";
+    $remainingMessage = $attemptButton ? "<p>$attemptButton</p>" : '';
 }
 
 $total_score = 0;
@@ -192,7 +196,7 @@ ExerciseLib::exercise_time_control_delete(
 
 ExerciseLib::delete_chat_exercise_session($exe_id);
 
-if ($origin != 'learnpath') {
+if (!in_array($origin, ['learnpath', 'embeddable'])) {
     echo '<div class="question-return">';
     echo Display::url(
         get_lang('ReturnToCourseHomepage'),
@@ -209,6 +213,18 @@ if ($origin != 'learnpath') {
         Session::erase('duration_time');
     }
     Display::display_footer();
+} elseif ($origin == 'embeddable') {
+    if (api_is_allowed_to_session_edit()) {
+        Session::erase('objExercise');
+        Session::erase('exe_id');
+        Session::erase('calculatedAnswerId');
+        Session::erase('duration_time_previous');
+        Session::erase('duration_time');
+    }
+
+    Session::write('attempt_remaining', $remainingMessage);
+
+    Display::display_reduced_footer();
 } else {
     $lp_mode = Session::read('lp_mode');
     $url = '../lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.$learnpath_id.'&lp_item_id='.$learnpath_item_id.'&exeId='.$exercise_stat_info['exe_id'].'&fb_type='.$objExercise->feedback_type.'#atoc_'.$learnpath_item_id;

+ 13 - 0
main/exercise/exercise_show.php

@@ -960,6 +960,19 @@ foreach ($questionList as $questionId) {
 
 $totalScoreText = '';
 
+if ($answerType != MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY) {
+    $pluginEvaluation = QuestionOptionsEvaluationPlugin::create();
+
+    if ('true' === $pluginEvaluation->get(QuestionOptionsEvaluationPlugin::SETTING_ENABLE)) {
+        $formula = $pluginEvaluation->getFormulaForExercise($objExercise->selectId());
+
+        if (!empty($formula)) {
+            $totalScore = $pluginEvaluation->getResultWithFormula($id, $formula);
+            $totalWeighting = $pluginEvaluation->getMaxScore();
+        }
+    }
+}
+
 // Total score
 $myTotalScoreTemp = $totalScore;
 if ($origin != 'learnpath' || ($origin == 'learnpath' && isset($_GET['fb_type']))) {

+ 16 - 16
main/exercise/exercise_submit.php

@@ -134,7 +134,7 @@ if (!isset($exerciseInSession) || isset($exerciseInSession) && ($exerciseInSessi
 
     // if the specified exercise doesn't exist or is disabled
     if (!$objExercise->read($exerciseId) ||
-        (!$objExercise->selectStatus() && !$is_allowedToEdit && $origin != 'learnpath')
+        (!$objExercise->selectStatus() && !$is_allowedToEdit && !in_array($origin, ['learnpath', 'embeddable']))
     ) {
         if ($debug) {
             error_log('1.1. Error while reading the exercise');
@@ -181,7 +181,7 @@ if ($objExercise->review_answers) {
 }
 
 $template->assign('shuffle_answers', $objExercise->random_answers);
-$templateName = $template->get_template('exercise/submit.js.html.twig');
+$templateName = $template->get_template('exercise/submit.js.tpl');
 $htmlHeadXtra[] = $template->fetch($templateName);
 
 $current_timestamp = time();
@@ -235,7 +235,7 @@ if ($objExercise->selectAttempts() > 0) {
     if ($attempt_count >= $objExercise->selectAttempts()) {
         $show_clock = false;
         if (!api_is_allowed_to_edit(null, true)) {
-            if ($objExercise->results_disabled == 0 && $origin != 'learnpath') {
+            if ($objExercise->results_disabled == 0 && !in_array($origin, ['learnpath', 'embeddable'])) {
                 // Showing latest attempt according with task BT#1628
                 $exercise_stat_info = Event::getExerciseResultsByUser(
                     $user_id,
@@ -247,7 +247,7 @@ if ($objExercise->selectAttempts() > 0) {
                 if (!empty($exercise_stat_info)) {
                     $isQuestionsLimitReached = ExerciseLib::isQuestionsLimitPerDayReached(
                         $user_id,
-                        $objExercise->selectNbrQuestions(),
+                        count($objExercise->get_validated_question_list()),
                         $courseId,
                         $sessionId
                     );
@@ -263,7 +263,7 @@ if ($objExercise->selectAttempts() > 0) {
                             )
                         );
 
-                        if ($origin == 'learnpath') {
+                        if (in_array($origin, ['learnpath', 'embeddable'])) {
                             Display::display_reduced_header();
                             Display::display_reduced_footer();
                         } else {
@@ -319,7 +319,7 @@ if ($objExercise->selectAttempts() > 0) {
             $attempt_html .= $messageReachedMax;
         }
 
-        if ($origin == 'learnpath') {
+        if (in_array($origin, ['learnpath', 'embeddable'])) {
             Display::display_reduced_header();
         } else {
             Display::display_header(get_lang('Exercises'));
@@ -327,7 +327,7 @@ if ($objExercise->selectAttempts() > 0) {
 
         echo $attempt_html;
 
-        if ($origin != 'learnpath') {
+        if (!in_array($origin, ['learnpath', 'embeddable'])) {
             Display::display_footer();
         }
         exit;
@@ -724,7 +724,7 @@ if ($formSent && isset($_POST)) {
                             'warning',
                             false
                         );
-                        if ($origin != 'learnpath') {
+                        if (!in_array($origin, ['learnpath', 'embeddable'])) {
                             //so we are not in learnpath tool
                             echo '</div>'; //End glossary div
                             Display::display_footer();
@@ -797,7 +797,7 @@ if ($question_count != 0) {
                             'warning',
                             false
                         );
-                        if ($origin != 'learnpath') {
+                        if (!in_array($origin, ['learnpath', 'embeddable'])) {
                             //so we are not in learnpath tool
                             echo '</div>'; //End glossary div
                             Display::display_footer();
@@ -860,7 +860,7 @@ $interbreadcrumb[] = [
 ];
 $interbreadcrumb[] = ["url" => "#", "name" => $objExercise->selectTitle(true)];
 
-if ($origin != 'learnpath') { //so we are not in learnpath tool
+if (!in_array($origin, ['learnpath', 'embeddable'])) { //so we are not in learnpath tool
     if (!api_is_allowed_to_session_edit()) {
         Display::addFlash(
             Display::return_message(get_lang('SessionIsReadOnly'), 'warning')
@@ -877,7 +877,7 @@ if ($origin != 'learnpath') { //so we are not in learnpath tool
 $show_quiz_edition = $objExercise->added_in_lp();
 
 // I'm in a preview mode
-if (api_is_course_admin() && $origin != 'learnpath') {
+if (api_is_course_admin() && !in_array($origin, ['learnpath', 'embeddable'])) {
     echo '<div class="actions">';
     if ($show_quiz_edition == false) {
         echo '<a href="exercise_admin.php?'.api_get_cidreq().'&modifyExercise=yes&exerciseId='.$objExercise->id.'">'.
@@ -897,7 +897,7 @@ $is_visible_return = $objExercise->is_visible(
 
 if ($is_visible_return['value'] == false) {
     echo $is_visible_return['message'];
-    if ($origin != 'learnpath') {
+    if (!in_array($origin, ['learnpath', 'embeddable'])) {
         Display :: display_footer();
     }
     exit;
@@ -933,7 +933,7 @@ if ($limit_time_exists) {
                 ),
                 'warning'
             );
-            if ($origin != 'learnpath') {
+            if (!in_array($origin, ['learnpath', 'embeddable'])) {
                 Display::display_footer();
             }
             exit;
@@ -965,7 +965,7 @@ if (isset($_custom['exercises_hidden_when_no_start_date']) &&
             ),
             'warning'
         );
-        if ($origin != 'learnpath') {
+        if (!in_array($origin, ['learnpath', 'embeddable'])) {
             Display :: display_footer();
         }
     }
@@ -978,7 +978,7 @@ if ($time_control) {
         get_lang('ExerciseExpiredTimeMessage').'</div>';
 }
 
-if ($origin != 'learnpath') {
+if (!in_array($origin, ['learnpath', 'embeddable'])) {
     echo '<div id="highlight-plugin" class="glossary-content">';
 }
 
@@ -1546,7 +1546,7 @@ if (!empty($error)) {
     echo '</form>';
 }
 
-if ($origin != 'learnpath') {
+if (!in_array($origin, ['learnpath', 'embeddable'])) {
     // So we are not in learnpath tool
     echo '</div>'; //End glossary div
 }

+ 34 - 6
main/exercise/export/aiken/aiken_import.inc.php

@@ -179,8 +179,11 @@ function aiken_import_exercise($file)
     $exercise->exercise = $exercise_info['name'];
     $exercise->save();
     $last_exercise_id = $exercise->selectId();
+    $tableQuestion = Database::get_course_table(TABLE_QUIZ_QUESTION);
+    $tableAnswer = Database::get_course_table(TABLE_QUIZ_ANSWER);
     if (!empty($last_exercise_id)) {
         // For each question found...
+        $courseId = api_get_course_int_id();
         foreach ($exercise_info['question'] as $key => $question_array) {
             // 2.create question
             $question = new Aiken2Question();
@@ -191,14 +194,13 @@ function aiken_import_exercise($file)
             if (isset($question_array['description'])) {
                 $question->updateDescription($question_array['description']);
             }
-
             $type = $question->selectType();
             $question->type = constant($type);
             $question->save($exercise);
-
             $last_question_id = $question->selectId();
             //3. Create answer
-            $answer = new Answer($last_question_id);
+            // 3. Create answer
+            $answer = new Answer($last_question_id, $courseId, $exercise, false);
             $answer->new_nbrAnswers = count($question_array['answer']);
             $max_score = 0;
 
@@ -211,6 +213,7 @@ function aiken_import_exercise($file)
                 $key++;
                 $answer->new_answer[$key] = $answers['value'];
                 $answer->new_position[$key] = $key;
+                $answer->new_comment[$key] = '';
                 // Correct answers ...
                 if (in_array($key, $question_array['correct_answers'])) {
                     $answer->new_correct[$key] = 1;
@@ -229,16 +232,41 @@ function aiken_import_exercise($file)
                 if (!empty($scoreFromFile) && $answer->new_correct[$key]) {
                     $answer->new_weighting[$key] = $scoreFromFile;
                 }
+
+                $params = [
+                    'c_id' => $courseId,
+                    'question_id' => $last_question_id,
+                    'answer' => $answer->new_answer[$key],
+                    'correct' => $answer->new_correct[$key],
+                    'comment' => $answer->new_comment[$key],
+                    'ponderation' => isset($answer->new_weighting[$key]) ? $answer->new_weighting[$key] : '',
+                    'position' => $answer->new_position[$key],
+                    'hotspot_coordinates' => '',
+                    'hotspot_type' => '',
+                ];
+
+                $answerId = Database::insert($tableAnswer, $params);
+                if ($answerId) {
+                    $params = [
+                        'id_auto' => $answerId,
+                        'id' => $answerId,
+                    ];
+                    Database::update($tableAnswer, $params, ['iid = ?' => [$answerId]]);
+                }
             }
 
             if (!empty($scoreFromFile)) {
                 $max_score = $scoreFromFile;
             }
 
-            $answer->save();
             // Now that we know the question score, set it!
-            $question->updateWeighting($max_score);
-            $question->save($exercise);
+
+            $params = ['ponderation' => $max_score];
+            Database::update(
+                $tableQuestion,
+                $params,
+                ['iid = ?' => [$last_question_id]]
+            );
         }
 
         // Delete the temp dir where the exercise was unzipped

+ 4 - 8
main/exercise/hotpotatoes.php

@@ -9,6 +9,9 @@
  * @author Istvan Mandak (original author)
  */
 require_once __DIR__.'/../inc/global.inc.php';
+
+api_protect_course_script(true);
+
 require_once 'hotpotatoes.lib.php';
 
 // Section (for the tabs).
@@ -131,14 +134,7 @@ if ((api_is_allowed_to_edit(null, true)) && (($finish == 0) || ($finish == 2)))
                 $filename = api_replace_dangerous_char(trim($_FILES['userFile']['name']));
                 $fld = GenerateHpFolder($document_sys_path.$uploadPath.'/');
                 @mkdir($document_sys_path.$uploadPath.'/'.$fld, api_get_permissions_for_new_directories());
-                $doc_id = DocumentManager::addDocument($_course, '/HotPotatoes_files/'.$fld, 'folder', 0, $fld);
-            /*api_item_property_update(
-                $_course,
-                TOOL_DOCUMENT,
-                $doc_id,
-                'FolderCreated',
-                api_get_user_id()
-            );*/
+                $doc_id = DocumentManager::addDocument($_course, '/HotPotatoes_files/'.$fld, 'folder', 0, $fld);   
             } else {
                 // It is not the first step... get the filename directly from the system params.
                 $filename = $_FILES['userFile']['name'];

+ 2 - 0
main/exercise/hotspot_actionscript.as.php

@@ -16,6 +16,8 @@ session_cache_limiter('none');
 
 require_once __DIR__.'/../inc/global.inc.php';
 
+api_protect_course_script(true);
+
 $_course = api_get_course_info();
 require api_get_path(LIBRARY_PATH).'geometry.lib.php';
 

+ 1 - 1
main/exercise/hotspot_admin.inc.php

@@ -608,7 +608,7 @@ if (isset($modifyAnswers)) {
         <input type="hidden" name="formSent" value="1"/>
         <input type="hidden" name="nbrAnswers" value="<?php echo $nbrAnswers; ?>"/>
         <div class="table-responsive">
-            <table class="table table-hover">
+            <table class="table table-striped table-hover">
                 <thead>
                 <tr>
                     <th width="5">&nbsp;</th>

+ 1 - 1
main/exercise/hotspot_answers.as.php

@@ -51,7 +51,7 @@ if (!api_is_allowed_to_edit(null, true)) {
     }
 }
 
-$objQuestion = Question::read($questionId, $courseId);
+$objQuestion = Question::read($questionId, $objExercise->course);
 $objExercise->read($exerciseId);
 
 if (empty($objQuestion) || empty($objExercise)) {

+ 2 - 0
main/exercise/hotspot_save.inc.php

@@ -10,6 +10,8 @@
  */
 require_once __DIR__.'/../inc/global.inc.php';
 
+api_protect_course_script(true);
+
 $TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
 $questionId = intval($_GET['questionId']);
 $answerId = intval($_GET['answerId']);

+ 3 - 0
main/exercise/hotspot_savescore.inc.php

@@ -13,6 +13,9 @@ use ChamiloSession as Session;
  * 	@version $Id: admin.php 10680 2007-01-11 21:26:23Z pcool $
  */
 require_once __DIR__.'/../inc/global.inc.php';
+
+api_protect_course_script(true);
+
 $courseCode = $_GET['coursecode'];
 $questionId = $_GET['questionId'];
 $coordinates = $_GET['coord'];

+ 2 - 0
main/exercise/hotspot_updatescore.inc.php

@@ -14,6 +14,8 @@ use ChamiloSession as Session;
  */
 require_once __DIR__.'/../inc/global.inc.php';
 
+api_protect_course_script(true);
+
 $courseCode = $_GET['coursecode'];
 $questionId = $_GET['questionId'];
 $coordinates = $_GET['coord'];

+ 2 - 2
main/exercise/matching.class.php

@@ -103,7 +103,7 @@ class Matching extends Question
         $form->addElement('hidden', 'nb_options', $nb_options);
 
         // DISPLAY MATCHES
-        $html = '<table class="table table-hover">
+        $html = '<table class="table table-striped table-hover">
             <thead>
                 <tr>
                     <th width="5%">'.get_lang('Number').'</th>
@@ -175,7 +175,7 @@ class Matching extends Question
         $form->addHtml('</tbody></table>');
 
         // DISPLAY OPTIONS
-        $html = '<table class="table table-hover">
+        $html = '<table class="table table-striped table-hover">
             <thead>
                 <tr>
                     <th width="15%">'.get_lang('Number').'</th>

+ 1 - 1
main/exercise/multiple_answer.class.php

@@ -47,7 +47,7 @@ class MultipleAnswer extends Question
 
         $form->addHeader(get_lang('Answers'));
 
-        $html = '<table class="table table-hover">
+        $html = '<table class="table table-striped table-hover">
             <thead>
                 <tr>
                     <th width="10">'.get_lang('Number').'</th>

+ 1 - 1
main/exercise/multiple_answer_combination.class.php

@@ -38,7 +38,7 @@ class MultipleAnswerCombination extends Question
         $nb_answers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0));
         $obj_ex = Session::read('objExercise');
 
-        $html = '<table class="table table-hover">';
+        $html = '<table class="table table-striped table-hover">';
         $html .= '<thead>';
         $html .= '<tr>';
         $html .= '<th width="10">'.get_lang('Number').'</th>';

+ 1 - 1
main/exercise/multiple_answer_true_false.class.php

@@ -43,7 +43,7 @@ class MultipleAnswerTrueFalse extends Question
         $renderer = &$form->defaultRenderer();
         $defaults = [];
 
-        $html = '<table class="table table-hover">';
+        $html = '<table class="table table-striped table-hover">';
         $html .= '<thead>';
         $html .= '<tr>';
         $html .= '<th>'.get_lang('Number').'</th>';

+ 2 - 2
main/exercise/overview.php

@@ -28,7 +28,7 @@ $sessionId = api_get_session_id();
 $exercise_id = isset($_REQUEST['exerciseId']) ? intval($_REQUEST['exerciseId']) : 0;
 
 $objExercise = new Exercise();
-$result = $objExercise->read($exercise_id, false);
+$result = $objExercise->read($exercise_id, true);
 
 if (!$result) {
     api_not_allowed(true);
@@ -72,7 +72,7 @@ if ($time_control) {
     $htmlHeadXtra[] = $objExercise->showTimeControlJS($time_left);
 }
 
-if ($origin != 'learnpath') {
+if (!in_array($origin, ['learnpath', 'embeddable'])) {
     Display::display_header();
 } else {
     $htmlHeadXtra[] = "

+ 2 - 0
main/exercise/qti2.php

@@ -12,6 +12,8 @@
  */
 require_once __DIR__.'/../inc/global.inc.php';
 
+api_protect_course_script(true);
+
 // section (for the tabs)
 $this_section = SECTION_COURSES;
 

+ 32 - 21
main/exercise/question.class.php

@@ -124,21 +124,19 @@ abstract class Question
      * Reads question information from the data base.
      *
      * @param int $id        - question ID
-     * @param int $course_id
+     * @param array $course_info
+     * @param bool  $getExerciseList
      *
      * @return Question
      *
      * @author Olivier Brouckaert
      */
-    public static function read($id, $course_id = null)
+    public static function read($id, $course_info = [], $getExerciseList = true)
     {
         $id = (int) $id;
-        if (!empty($course_id)) {
-            $course_info = api_get_course_info_by_id($course_id);
-        } else {
+        if (empty($course_info)) {
             $course_info = api_get_course_info();
         }
-
         $course_id = $course_info['real_id'];
 
         if (empty($course_id) || $course_id == -1) {
@@ -172,22 +170,24 @@ abstract class Question
                 $objQuestion->category = TestCategory::getCategoryForQuestion($id, $course_id);
                 $objQuestion->code = isset($object->code) ? $object->code : '';
 
-                $tblQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
-                $sql = "SELECT DISTINCT q.exercice_id
-                        FROM $TBL_EXERCISE_QUESTION q
-                        INNER JOIN $tblQuiz e
-                        ON e.c_id = q.c_id AND e.id = q.exercice_id
-                        WHERE
-                            q.c_id = $course_id AND
-                            q.question_id = $id AND
-                            e.active >= 0";
+                if ($getExerciseList) {
+                    $tblQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
+                    $sql = "SELECT DISTINCT q.exercice_id
+                            FROM $TBL_EXERCISE_QUESTION q
+                            INNER JOIN $tblQuiz e
+                            ON e.c_id = q.c_id AND e.id = q.exercice_id
+                            WHERE
+                                q.c_id = $course_id AND
+                                q.question_id = $id AND
+                                e.active >= 0";
 
-                $result = Database::query($sql);
+                    $result = Database::query($sql);
 
-                // fills the array with the exercises which this question is in
-                if ($result) {
-                    while ($obj = Database::fetch_object($result)) {
-                        $objQuestion->exerciseList[] = $obj->exercice_id;
+                    // fills the array with the exercises which this question is in
+                    if ($result) {
+                        while ($obj = Database::fetch_object($result)) {
+                            $objQuestion->exerciseList[] = $obj->exercice_id;
+                        }
                     }
                 }
 
@@ -1981,16 +1981,27 @@ abstract class Question
      *
      * @return string HTML string with the header of the question (before the answers table)
      */
-    public function return_header($exercise, $counter = null, $score = [])
+    public function return_header(Exercise $exercise, $counter = null, $score = [])
     {
         $counterLabel = '';
         if (!empty($counter)) {
             $counterLabel = (int) $counter;
         }
+
         $scoreLabel = get_lang('Wrong');
+
+        if ($exercise->results_disabled == RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER) {
+            $scoreLabel = get_lang('QuizWrongAnswerHereIsTheCorrectOne');
+        }
+
         $class = 'error';
         if (isset($score['pass']) && $score['pass'] == true) {
             $scoreLabel = get_lang('Correct');
+
+            if ($exercise->results_disabled == RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER) {
+                $scoreLabel = get_lang('CorrectAnswer');
+            }
+
             $class = 'success';
         }
 

+ 62 - 61
main/exercise/question_list_admin.inc.php

@@ -159,7 +159,7 @@ Session::erase('less_answer');
 // If we are in a test
 $inATest = isset($exerciseId) && $exerciseId > 0;
 if (!$inATest) {
-    echo "<div class='alert alert-warning'>".get_lang('ChoiceQuestionType')."</div>";
+    echo Display::return_message(get_lang('ChoiceQuestionType'), 'warning');
 } else {
     if ($nbrQuestions) {
         // In the building exercise mode show question list ordered as is.
@@ -167,15 +167,13 @@ if (!$inATest) {
 
         // In building mode show all questions not render by teacher order.
         $objExercise->questionSelectionType = EX_Q_SELECTION_ORDERED;
-        $alloQuestionOrdering = true;
+        $allowQuestionOrdering = true;
         $showPagination = api_get_configuration_value('show_question_pagination');
         if (!empty($showPagination) && $nbrQuestions > $showPagination) {
-            // $page is declare in admin.php
-            //$page = isset($_GET['page']) && !empty($_GET['page']) ? (int) $_GET['page'] : 1;
             $length = api_get_configuration_value('question_pagination_length');
             $url = api_get_self().'?'.api_get_cidreq();
             // Use pagination for exercise with more than 200 questions.
-            $alloQuestionOrdering = false;
+            $allowQuestionOrdering = false;
             $start = ($page - 1) * $length;
             $questionList = $objExercise->getQuestionForTeacher($start, $length);
             $paginator = new Knp\Component\Pager\Paginator();
@@ -204,8 +202,13 @@ if (!$inATest) {
         }
 
         echo '
-        <div class="list-header-question">
-            <h2>'.get_lang('List Questions').'</h2>
+            <div class="row hidden-xs">
+                <div class="col-sm-5"><strong>'.get_lang('Questions').'</strong></div>
+                <div class="col-sm-1 text-center"><strong>'.get_lang('Type').'</strong></div>
+                <div class="col-sm-2"><strong>'.get_lang('Category').'</strong></div>
+                <div class="col-sm-1 text-right"><strong>'.get_lang('Difficulty').'</strong></div>
+                <div class="col-sm-1 text-right"><strong>'.get_lang('MaximumScore').'</strong></div>
+                <div class="col-sm-2 text-right"><strong>'.get_lang('Actions').'</strong></div>
         </div>
         <div id="question_list">
     ';
@@ -221,53 +224,60 @@ if (!$inATest) {
                 /** @var Question $objQuestionTmp */
                 $objQuestionTmp = Question::read($id);
 
-                if (empty($objQuestionTmp)) {
-                    continue;
-                }
-
                 $clone_link = Display::url(
-                    Display::returnFontAwesomeIcon('copy'),
-                    api_get_self().'?'.api_get_cidreq().'&clone_question='.$id,
-                    [
-                        'class' => 'btn btn-outline-secondary btn-sm',
-                        'title' => get_lang('Copy'),
-                    ]
+                    Display::return_icon(
+                        'cd.png',
+                        get_lang('Copy'),
+                        [],
+                        ICON_SIZE_TINY
+                    ),
+                    api_get_self().'?'.api_get_cidreq().'&clone_question='.$id.'&page='.$page,
+                    ['class' => 'btn btn-default btn-sm']
                 );
                 $edit_link = $objQuestionTmp->type == CALCULATED_ANSWER && $objQuestionTmp->isAnswered()
                     ? Display::span(
-                        Display::returnFontAwesomeIcon('pencil-alt'),
-                        [
-                           'class' => 'btn btn-outline-secondary btn-sm',
-                           'title' => get_lang('QuestionEditionNotAvailableBecauseItIsAlreadyAnsweredHoweverYouCanCopyItAndModifyTheCopy'),
-                           'disabled',
-                        ]
+                        Display::return_icon(
+                            'edit_na.png',
+                            get_lang('QuestionEditionNotAvailableBecauseItIsAlreadyAnsweredHoweverYouCanCopyItAndModifyTheCopy'),
+                            [],
+                            ICON_SIZE_TINY
+                        ),
+                        ['class' => 'btn btn-default btn-sm']
                     )
                     : Display::url(
-                        Display::returnFontAwesomeIcon('pencil-alt'),
+                        Display::return_icon(
+                            'edit.png',
+                            get_lang('Modify'),
+                            [],
+                            ICON_SIZE_TINY
+                        ),
                         api_get_self().'?'.api_get_cidreq().'&'
                             .http_build_query([
                                 'type' => $objQuestionTmp->selectType(),
                                 'myid' => 1,
                                 'editQuestion' => $id,
+                                'page' => $page,
                             ]),
-                        [
-                            'class' => 'btn btn-outline-secondary btn-sm',
-                            'title' => get_lang('Modify'),
-                        ]
+                        ['class' => 'btn btn-default btn-sm']
                     );
                 $delete_link = null;
                 if ($objExercise->edit_exercise_in_lp == true) {
                     $delete_link = Display::url(
-                        Display::returnFontAwesomeIcon('trash-alt'),
+                        Display::return_icon(
+                            'delete.png',
+                            get_lang('RemoveFromTest'),
+                            [],
+                            ICON_SIZE_TINY
+                        ),
                         api_get_self().'?'.api_get_cidreq().'&'
                             .http_build_query([
                                 'exerciseId' => $exerciseId,
                                 'deleteQuestion' => $id,
+                                'page' => $page,
                             ]),
                         [
                             'id' => "delete_$id",
-                            'title' => get_lang('RemoveFromTest'),
-                            'class' => 'btn btn-outline-secondary btn-sm delete-swal',
+                            'class' => 'opener btn btn-default btn-sm',
                         ]
                     );
                 }
@@ -283,29 +293,20 @@ if (!$inATest) {
 
                 $title = Security::remove_XSS($objQuestionTmp->selectTitle());
                 $title = strip_tags($title);
-                $move = null;
-                if ($alloQuestionOrdering) {
-                    $move .= Display::tag(
-                        'div',
-                        Display::returnFontAwesomeIcon(
-                            'arrows-alt ',
-                            1,
-                            true),
-                        [
-                            'class' => 'btn-moved moved',
-                        ]
-                    );
+                $move = '&nbsp;';
+                if ($allowQuestionOrdering) {
+                    $move = Display::returnFontAwesomeIcon('arrows moved', 1, true);
                 }
 
                 // Question name
                 $questionName =
                     '<a href="#" title = "'.Security::remove_XSS($title).'">
-                        '.cut($title, 42).'
+                        '.$move.' '.cut($title, 42).'
                     </a>';
 
                 // Question type
                 list($typeImg, $typeExpl) = $objQuestionTmp->get_type_icon_html();
-                $questionType = Display::return_icon($typeImg, $typeExpl, [], ICON_SIZE_MEDIUM);
+                $questionType = Display::return_icon($typeImg, $typeExpl);
 
                 // Question category
                 $txtQuestionCat = Security::remove_XSS(
@@ -328,28 +329,28 @@ if (!$inATest) {
                 echo '<div id="question_id_list_'.$id.'">
                         <div class="header_operations" data-exercise="'.$objExercise->selectId().'"
                             data-question="'.$id.'">
-                            <div class="card">
-                                <div class="card-body">
                                      <div class="row">
-                                        <div class="col-md-1">
-                                            '.$move.'
+                                <div class="question col-sm-5 col-xs-12">'
+                                    .$questionName.'
                                         </div>
-                                        <div class="col-md-5">
-                                            <div class="question-title">
-                                                '.$questionType.$questionName.'
+                                <div class="type text-center col-sm-1 col-xs-12">
+                                    <span class="visible-xs-inline">'.get_lang('Type').' </span>'
+                                    .$questionType.'
                                             </div>
+                                <div class="category col-sm-2 col-xs-12" title="'.$txtQuestionCat.'">
+                                    <span class="visible-xs-inline">'.get_lang('Category').' </span>'
+                                    .cut($txtQuestionCat, 42).'
                                         </div>
-                                        <div class="col-md-4">
-                                            <div class="d-flex flex-row bd-highlight mt-2 mb-2">
-                                                <div class="p-2 bd-highlight">'.get_lang('Category').': '.cut($txtQuestionCat, 42).'</div>
-                                                <div class="p-2 bd-highlight">'.get_lang('Difficulty').': '.$questionLevel.'</div>
-                                                <div class="p-2 bd-highlight">'.get_lang('Score').': '.$questionScore.'</div>
+                                <div class="level text-right col-sm-1 col-xs-6">
+                                    <span class="visible-xs-inline">'.get_lang('Difficulty').' </span>'
+                                    .$questionLevel.'
                                             </div>
+                                <div class="score text-right col-sm-1 col-xs-6">
+                                    <span class="visible-xs-inline">'.get_lang('Score').' </span>'
+                                    .$questionScore.'
                                         </div>
-                                        <div class="col-md-2">
-                                            <div class="text-right mt-2 mb-2 mr-2">'.$btnActions.'</div>
-                                        </div>
-                                     </div>
+                                <div class="btn-actions text-right col-sm-2 col-xs-6">
+                                    <div class="edition">'.$btnActions.'</div>
                                 </div>                     
                             </div>
                         </div>

+ 9 - 7
main/exercise/question_pool.php

@@ -17,6 +17,8 @@ use Knp\Component\Pager\Paginator;
  */
 require_once __DIR__.'/../inc/global.inc.php';
 
+api_protect_course_script(true);
+
 $this_section = SECTION_COURSES;
 
 $is_allowedToEdit = api_is_allowed_to_edit(null, true);
@@ -45,16 +47,15 @@ if (!isset($_GET['selected_course']) && !isset($_GET['exerciseId'])) {
 }
 
 $_course = api_get_course_info();
-
-if (empty($objExercise) && !empty($fromExercise)) {
-    $objExercise = new Exercise();
+$objExercise = new Exercise();
+if (!empty($fromExercise)) {
     $objExercise->read($fromExercise, false);
 }
 
 $nameTools = get_lang('QuestionPool');
 $interbreadcrumb[] = ['url' => 'exercise.php?'.api_get_cidreq(), 'name' => get_lang('Exercises')];
 
-if (!empty($objExercise)) {
+if (!empty($objExercise->id)) {
     $interbreadcrumb[] = [
         'url' => 'admin.php?exerciseId='.$objExercise->id.'&'.api_get_cidreq(),
         'name' => $objExercise->selectTitle(true),
@@ -71,7 +72,7 @@ if ($is_allowedToEdit) {
         $current_course = api_get_course_info();
         $old_question_id = $question_copy;
         // Reading the source question
-        $old_question_obj = Question::read($old_question_id, $origin_course_id);
+        $old_question_obj = Question::read($old_question_id, $origin_course_info);
         $courseId = $current_course['real_id'];
         if ($old_question_obj) {
             $old_question_obj->updateTitle($old_question_obj->selectTitle().' - '.get_lang('Copy'));
@@ -141,7 +142,7 @@ if ($is_allowedToEdit) {
             $current_course = api_get_course_info();
             foreach ($question_data as $old_question_id) {
                 //Reading the source question
-                $old_question_obj = Question::read($old_question_id, $origin_course_id);
+                $old_question_obj = Question::read($old_question_id, $origin_course_info);
                 if ($old_question_obj) {
                     $old_question_obj->updateTitle(
                         $old_question_obj->selectTitle().' - '.get_lang('Copy')
@@ -1040,7 +1041,8 @@ function get_action_icon_for_question(
  */
 function get_question_type_for_question($in_selectedcourse, $in_questionid)
 {
-    $myObjQuestion = Question::read($in_questionid, $in_selectedcourse);
+    $courseInfo = api_get_course_info_by_id($in_selectedcourse);
+    $myObjQuestion = Question::read($in_questionid, $courseInfo);
     $questionType = null;
     if (!empty($myObjQuestion)) {
         list($typeImg, $typeExpl) = $myObjQuestion->get_type_icon_html();

+ 3 - 1
main/exercise/recalculate.php

@@ -5,6 +5,8 @@ use Chamilo\CoreBundle\Entity\TrackEExercises;
 
 require_once __DIR__.'/../inc/global.inc.php';
 
+api_protect_course_script(true);
+
 $isAllowedToEdit = api_is_allowed_to_edit(true, true);
 
 if (!$isAllowedToEdit) {
@@ -62,7 +64,7 @@ if ('true' === $pluginEvaluation->get(QuestionOptionsEvaluationPlugin::SETTING_E
 
 if (!$useEvaluationPlugin) {
     foreach ($questionList as $questionId) {
-        $question = Question::read($questionId, $courseId);
+        $question = Question::read($questionId, api_get_course_info());
         $totalWeight += $question->selectWeighting();
 
         // We're inside *one* question. Go through each possible answer for this question

+ 1 - 1
main/exercise/result.php

@@ -14,7 +14,7 @@ $id = isset($_REQUEST['id']) ? (int) $_GET['id'] : null; //exe id
 $show_headers = isset($_REQUEST['show_headers']) ? (int) $_REQUEST['show_headers'] : null;
 $origin = api_get_origin();
 
-if ($origin == 'learnpath') {
+if (in_array($origin, ['learnpath', 'embeddable'])) {
     $show_headers = false;
 }
 

+ 3 - 0
main/exercise/savescores.php

@@ -7,6 +7,9 @@
  * @package chamilo.exercise
  */
 require_once __DIR__.'/../inc/global.inc.php';
+
+api_protect_course_script(true);
+
 $courseInfo = api_get_course_info();
 $_user = api_get_user_info();
 

+ 3 - 0
main/exercise/showinframes.php

@@ -9,6 +9,9 @@
  * @author Istvan Mandak
  */
 require_once __DIR__.'/../inc/global.inc.php';
+
+api_protect_course_script(true);
+
 require_once api_get_path(SYS_CODE_PATH).'exercise/hotpotatoes.lib.php';
 $_course = api_get_course_info();
 

+ 3 - 0
main/exercise/tests_category.php

@@ -25,9 +25,12 @@ require_once __DIR__.'/../inc/global.inc.php';
 
 $this_section = SECTION_COURSES;
 
+api_protect_course_script(true);
+
 if (!api_is_allowed_to_edit()) {
     api_not_allowed(true);
 }
+
 $category = new TestCategory();
 $courseId = api_get_course_int_id();
 $sessionId = api_get_session_id();

+ 1 - 1
main/exercise/unique_answer.class.php

@@ -66,7 +66,7 @@ class UniqueAnswer extends Question
             $comment_title = '<th width="40%">'.get_lang('Comment').'</th>';
         }
 
-        $html = '<table class="table table-hover">
+        $html = '<table class="table table-striped table-hover">
             <thead>
                 <tr style="text-align: center;">
                     <th width="5%">'.get_lang('Number').'</th>

+ 1 - 1
main/exercise/unique_answer_no_option.class.php

@@ -63,7 +63,7 @@ class UniqueAnswerNoOption extends Question
             $comment_title = '<th width="50%">'.get_lang('Comment').'</th>';
         }
 
-        $html = '<table class="table table-hover">';
+        $html = '<table class="table table-striped table-hover">';
         $html .= '<thead>';
         $html .= '<tr>';
         $html .= '<th>'.get_lang('Number').'</th>';

+ 6 - 4
main/exercise/upload_exercise.php

@@ -14,6 +14,8 @@ $help_content = 'exercise_upload';
 
 require_once __DIR__.'/../inc/global.inc.php';
 
+api_protect_course_script(true);
+
 $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
 $debug = false;
 $origin = api_get_origin();
@@ -436,7 +438,7 @@ function lp_upload_quiz_action_handling()
 
                             $questionObj = Question::read(
                                 $question_id,
-                                $courseId
+                                $_course
                             );
 
                             if ($questionObj) {
@@ -456,7 +458,7 @@ function lp_upload_quiz_action_handling()
                         break;
                     case FREE_ANSWER:
                         $globalScore = isset($scoreList[$i]) ? $scoreList[$i] : null;
-                        $questionObj = Question::read($question_id, $courseId);
+                        $questionObj = Question::read($question_id, $_course);
                         if ($questionObj) {
                             $questionObj->updateWeighting($globalScore);
                             $questionObj->save($exercise);
@@ -489,7 +491,7 @@ function lp_upload_quiz_action_handling()
 
                         $objAnswer->save();
 
-                        $questionObj = Question::read($question_id, $courseId);
+                        $questionObj = Question::read($question_id, $_course);
                         if ($questionObj) {
                             $questionObj->updateWeighting($globalScore);
                             $questionObj->save($exercise);
@@ -520,7 +522,7 @@ function lp_upload_quiz_action_handling()
                             $counter++;
                         }
                         $objAnswer->save();
-                        $questionObj = Question::read($question_id, $courseId);
+                        $questionObj = Question::read($question_id, $_course);
                         if ($questionObj) {
                             $questionObj->updateWeighting($globalScore);
                             $questionObj->save($exercise);

+ 1 - 1
main/extra/userInfo.php

@@ -359,7 +359,7 @@ if ($displayMode == "viewDefEdit") {
         // is the user online?
         $online = '';
         if (user_is_online($userIdViewed)) {
-            $online = Display::return_icon('online.png', get_lang('OnLine'), ['style' => 'with="8"; height="8"']);
+            $online = Display::return_icon('online.gif', get_lang('OnLine'), ['style' => 'with="8"; height="8"']);
         }
 
         // DISPLAY TABLE HEADING

+ 1 - 1
main/gamification/my_progress.php

@@ -76,7 +76,7 @@ $template = new Template($nameTools);
 $template->assign('user', $user);
 $template->assign(
     'user_avatar',
-    SocialManager::getAvatarBlock('home', 0, $user->getId())
+    SocialManager::show_social_avatar_block('home', 0, $user->getId())
 );
 $template->assign(
     'gamification_stars',

+ 12 - 5
main/inc/lib/CoursesAndSessionsCatalog.class.php

@@ -497,7 +497,7 @@ class CoursesAndSessionsCatalog
                     $urlCondition = ' (access_url_id = '.$urlId.' OR access_url_id = 1) AND ';
                 }
 
-                $sql = "SELECT *
+                $sql = "SELECT course.*
                             FROM $courseTable as course
                             INNER JOIN $tbl_url_rel_course as url_rel_course
                             ON (url_rel_course.c_id = course.id)
@@ -519,8 +519,15 @@ class CoursesAndSessionsCatalog
         $courses = [];
         while ($row = Database::fetch_array($result_find)) {
             $row['registration_code'] = !empty($row['registration_code']);
-            $count_users = count(CourseManager::get_user_list_from_course_code($row['code']));
-            $count_connections_last_month = Tracking::get_course_connections_count(
+            $countUsers = CourseManager::get_user_list_from_course_code(
+                $row['code'],
+                0,
+                null,
+                null,
+                null,
+                true
+            );
+            $connectionsLastMonth = Tracking::get_course_connections_count(
                 $row['id'],
                 0,
                 api_get_utc_datetime(time() - (30 * 86400))
@@ -541,8 +548,8 @@ class CoursesAndSessionsCatalog
                 'registration_code' => $row['registration_code'],
                 'creation_date' => $row['creation_date'],
                 'visibility' => $row['visibility'],
-                'count_users' => $count_users,
-                'count_connections' => $count_connections_last_month,
+                'count_users' => $countUsers,
+                'count_connections' => $connectionsLastMonth,
             ];
         }
 

+ 3 - 4
main/inc/lib/MoodleImport.php

@@ -85,11 +85,10 @@ class MoodleImport
                 break;
         }
 
-        $coursePath = api_get_course_path();
+        $courseInfo = api_get_course_info();
         $sessionId = api_get_session_id();
         $groupId = api_get_group_id();
-        $documentPath = api_get_path(SYS_COURSE_PATH).$coursePath.'/document';
-        $courseInfo = api_get_course_info();
+        $documentPath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
 
         create_unexisting_directory(
             $courseInfo,
@@ -270,7 +269,7 @@ class MoodleImport
                         // Replace the path from @@PLUGINFILE@@ to a correct chamilo path
                         $questionText = str_replace(
                             '@@PLUGINFILE@@',
-                            '/courses/'.$coursePath.'/document/moodle',
+                            '/courses/'.$courseInfo['path'].'/document/moodle',
                             $questionText
                         );
 

+ 40 - 58
main/inc/lib/agenda.lib.php

@@ -2942,62 +2942,41 @@ class Agenda
      */
     public function displayActions($view, $filter = 0)
     {
-        $courseInfo = api_get_course_info();
         $groupInfo = GroupManager::get_group_properties(api_get_group_id());
         $groupIid = isset($groupInfo['iid']) ? $groupInfo['iid'] : 0;
 
-        $courseCondition = '';
-        if (!empty($courseInfo)) {
-            $courseCondition = api_get_cidreq();
-        }
+        $codePath = api_get_path(WEB_CODE_PATH);
 
-        $actionsLeft = '';
-        $actionsLeft .= "<a href='".api_get_path(WEB_CODE_PATH)."calendar/agenda_js.php?type={$this->type}&".$courseCondition."'>".
-            Display::return_icon(
-                'calendar.png',
-                get_lang('Calendar'),
-                '',
-                ICON_SIZE_MEDIUM
-            )."</a>";
+        $currentUserId = api_get_user_id();
+        $cidReq = api_get_cidreq();
 
-        $actionsLeft .= "<a href='".api_get_path(WEB_CODE_PATH)."calendar/agenda_list.php?type={$this->type}&".$courseCondition."'>".
-            Display::return_icon(
-                'week.png',
-                get_lang('AgendaList'),
-                '',
-                ICON_SIZE_MEDIUM
-            )."</a>";
+        $actionsLeft = '';
+        $actionsLeft .= Display::url(
+            Display::return_icon('calendar.png', get_lang('Calendar'), [], ICON_SIZE_MEDIUM),
+            $codePath."calendar/agenda_js.php?type={$this->type}&$cidReq"
+        );
+        $actionsLeft .= Display::url(
+            Display::return_icon('week.png', get_lang('AgendaList'), [], ICON_SIZE_MEDIUM),
+            $codePath."calendar/agenda_list.php?type={$this->type}&$cidReq"
+        );
 
         $form = '';
         if (api_is_allowed_to_edit(false, true) ||
-            (api_get_course_setting('allow_user_edit_agenda') == '1' &&
-                !api_is_anonymous()) &&
-            api_is_allowed_to_session_edit(false, true) ||
-            (GroupManager::user_has_access(
-                api_get_user_id(),
-                $groupIid,
-                GroupManager::GROUP_TOOL_CALENDAR
-            ) &&
-            GroupManager::is_tutor_of_group(api_get_user_id(), $groupInfo))
+            (api_get_course_setting('allow_user_edit_agenda') == '1' && !api_is_anonymous()) &&
+            api_is_allowed_to_session_edit(false, true)
+            || (
+                GroupManager::user_has_access($currentUserId, $groupIid, GroupManager::GROUP_TOOL_CALENDAR)
+                && GroupManager::is_tutor_of_group($currentUserId, $groupInfo)
+            )
         ) {
             $actionsLeft .= Display::url(
-                Display::return_icon(
-                    'new_event.png',
-                    get_lang('AgendaAdd'),
-                    '',
-                    ICON_SIZE_MEDIUM
-                ),
-                api_get_path(WEB_CODE_PATH)."calendar/agenda.php?".api_get_cidreq()."&action=add&type=".$this->type
+                Display::return_icon('new_event.png', get_lang('AgendaAdd'), [], ICON_SIZE_MEDIUM),
+                $codePath."calendar/agenda.php?action=add&type={$this->type}&$cidReq"
             );
 
             $actionsLeft .= Display::url(
-                Display::return_icon(
-                    'import_calendar.png',
-                    get_lang('ICalFileImport'),
-                    '',
-                    ICON_SIZE_MEDIUM
-                ),
-                api_get_path(WEB_CODE_PATH)."calendar/agenda.php?".api_get_cidreq()."&action=importical&type=".$this->type
+                Display::return_icon('import_calendar.png', get_lang('ICalFileImport'), [], ICON_SIZE_MEDIUM),
+                $codePath."calendar/agenda.php?action=importical&type={$this->type}&$cidReq"
             );
 
             if ($this->type === 'course') {
@@ -3021,6 +3000,13 @@ class Agenda
             }
         }
 
+        if ($this->type == 'personal' && !api_is_anonymous()) {
+            $actionsLeft .= Display::url(
+                Display::return_icon('1day.png', get_lang('SessionsPlanCalendar'), [], ICON_SIZE_MEDIUM),
+                $codePath."calendar/planification.php"
+            );
+        }
+
         if (api_is_platform_admin() ||
             api_is_teacher() ||
             api_is_student_boss() ||
@@ -3040,26 +3026,20 @@ class Agenda
                         FormValidator::LAYOUT_INLINE
                     );
 
+                    $sessions = [];
+
                     if (api_is_drh()) {
-                        $sessionList = SessionManager::get_sessions_followed_by_drh(
-                            api_get_user_id()
-                        );
+                        $sessionList = SessionManager::get_sessions_followed_by_drh($currentUserId);
                         if (!empty($sessionList)) {
-                            $sessions = [];
                             foreach ($sessionList as $sessionItem) {
                                 $sessions[$sessionItem['id']] = strip_tags($sessionItem['name']);
                             }
                         }
                     } else {
-                        $sessions = SessionManager::get_sessions_by_user(
-                            api_get_user_id()
-                        );
-                        $sessions = array_column(
-                            $sessions,
-                            'session_name',
-                            'session_id'
-                        );
+                        $sessions = SessionManager::get_sessions_by_user($currentUserId);
+                        $sessions = array_column($sessions, 'session_name', 'session_id');
                     }
+
                     $form->addHidden('type', 'personal');
                     $sessions = ['0' => get_lang('Select An Option')] + $sessions;
 
@@ -3067,8 +3047,10 @@ class Agenda
                         'session_id',
                         get_lang('Session'),
                         $sessions,
-                        ['id' => 'session_id', 'onchange' => 'submit();', 'icon' => 'broom']
-                    )->setButton(true);
+                        ['id' => 'session_id', 'onchange' => 'submit();']
+                    );
+
+                    $form->addButtonReset(get_lang('Reset'));
                     $form = $form->returnForm();
                 }
             }
@@ -3098,7 +3080,7 @@ class Agenda
             api_get_self().'?action=importical&type='.$this->type,
             ['enctype' => 'multipart/form-data']
         );
-        $form->addElement('header', get_lang('ICalFileImport'));
+        $form->addHeader(get_lang('ICalFileImport'));
         $form->addElement('file', 'ical_import', get_lang('ICalFileImport'));
         $form->addRule(
             'ical_import',

+ 86 - 0
main/inc/lib/api.lib.php

@@ -9359,3 +9359,89 @@ function api_find_template($template)
 {
     return Template::findTemplateFilePath($template);
 }
+
+/**
+ * @return array
+ */
+function api_get_language_list_for_flag()
+{
+    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
+    $sql = "SELECT english_name, isocode FROM $table 
+            ORDER BY original_name ASC";
+    static $languages = [];
+    if (empty($languages)) {
+        $result = Database::query($sql);
+        while ($row = Database::fetch_array($result)) {
+            $languages[$row['english_name']] = $row['isocode'];
+        }
+        $languages['english'] = 'gb';
+    }
+
+    return $languages;
+}
+
+/**
+ * @return string
+ */
+function api_get_language_translate_html()
+{
+    $translate = api_get_configuration_value('translate_html');
+
+    if (!$translate) {
+        return '';
+    }
+
+    $languageList = api_get_languages();
+    $hideAll = '';
+    foreach ($languageList['all'] as $language) {
+        $hideAll .=
+            '$("span:lang('.$language['isocode'].')").filter(
+                function() {
+                    // Ignore ckeditor classes
+                    return !this.className.match(/cke(.*)/);
+            }).hide();';
+    }
+
+    $userInfo = api_get_user_info();
+    $languageId = api_get_language_id($userInfo['language']);
+    $languageInfo = api_get_language_info($languageId);
+
+    return '
+            $(function() {
+                '.$hideAll.'                 
+                var defaultLanguageFromUser = "'.$languageInfo['isocode'].'";   
+                                             
+                $("span:lang('.$languageInfo['isocode'].')").filter(
+                    function() {
+                        // Ignore ckeditor classes
+                        return !this.className.match(/cke(.*)/);
+                }).show();
+                
+                var defaultLanguage = "";
+                var langFromUserFound = false;
+                
+                $(this).find("span").filter(
+                    function() {
+                        // Ignore ckeditor classes
+                        return !this.className.match(/cke(.*)/);
+                }).each(function() {
+                    defaultLanguage = $(this).attr("lang");                            
+                    if (defaultLanguage) {
+                        $(this).before().next("br").remove();                
+                        if (defaultLanguageFromUser == defaultLanguage) {
+                            langFromUserFound = true;
+                        }
+                    }
+                });
+                
+                // Show default language
+                if (langFromUserFound == false && defaultLanguage) {
+                    $("span:lang("+defaultLanguage+")").filter(
+                    function() {
+                            // Ignore ckeditor classes
+                            return !this.className.match(/cke(.*)/);
+                    }).show();
+                }
+            });
+    ';
+}

+ 25 - 25
main/inc/lib/course.lib.php

@@ -1138,7 +1138,7 @@ class CourseManager
                 return false;
             }
             $courseId = $courseInfo['real_id'];
-            $condition_course = ' AND c_id = '.$courseId;
+            $condition_course = " AND c_id = $courseId";
         }
 
         $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
@@ -1160,22 +1160,22 @@ class CourseManager
         }
 
         $tableSessionCourseUser = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
-        $sql = 'SELECT 1 FROM '.$tableSessionCourseUser.'
-                WHERE user_id = '.$user_id.' AND session_id = '.$session_id.' '.$condition_course;
+        $sql = "SELECT 1 FROM $tableSessionCourseUser
+                WHERE user_id = $user_id AND session_id = $session_id $condition_course";
 
         if (Database::num_rows(Database::query($sql)) > 0) {
             return true;
         }
 
-        $sql = 'SELECT 1 FROM '.$tableSessionCourseUser.'
-                WHERE user_id = '.$user_id.' AND session_id = '.$session_id.' AND status = 2 '.$condition_course;
+        $sql = "SELECT 1 FROM $tableSessionCourseUser
+                WHERE user_id = $user_id AND session_id = $session_id AND status = 2 $condition_course";
 
         if (Database::num_rows(Database::query($sql)) > 0) {
             return true;
         }
 
         $sql = 'SELECT 1 FROM '.Database::get_main_table(TABLE_MAIN_SESSION).
-              ' WHERE id = '.$session_id.' AND id_coach='.$user_id;
+              " WHERE id = $session_id AND id_coach = $user_id";
 
         if (Database::num_rows(Database::query($sql)) > 0) {
             return true;
@@ -1346,7 +1346,7 @@ class CourseManager
         // we have to check if it is a valid field that can be sorted on
         if (!strstr($order_by, 'ORDER BY')) {
             if (!empty($order_by)) {
-                $order_by = 'ORDER BY '.$order_by;
+                $order_by = "ORDER BY $order_by";
             } else {
                 $order_by = '';
             }
@@ -1379,7 +1379,7 @@ class CourseManager
             if (!empty($courseCodeList)) {
                 $courseCodeListForSession = array_map(['Database', 'escape_string'], $courseCodeList);
                 $courseCodeListForSession = implode('","', $courseCodeListForSession);
-                $courseCondition = ' course.code IN ("'.$courseCodeListForSession.'")  ';
+                $courseCondition = " course.code IN ('$courseCodeListForSession')  ";
             }
 
             $sql .= ' FROM '.Database::get_main_table(TABLE_MAIN_USER).' as user ';
@@ -1434,7 +1434,7 @@ class CourseManager
                        ON course_rel_user.c_id = course.id ";
 
             if (!empty($course_code)) {
-                $sql .= ' AND course_rel_user.c_id = "'.$courseId.'"';
+                $sql .= " AND course_rel_user.c_id = $courseId";
             }
             $where[] = ' course_rel_user.c_id IS NOT NULL ';
 
@@ -1456,17 +1456,17 @@ class CourseManager
                 $extraFieldInfo = UserManager::get_extra_field_information_by_name($extraField);
                 if (!empty($extraFieldInfo)) {
                     $fieldValuesTable = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
-                    $sql .= ' LEFT JOIN '.$fieldValuesTable.' as ufv
+                    $sql .= " LEFT JOIN $fieldValuesTable as ufv
                             ON (
                                 user.id = ufv.item_id AND
-                                (field_id = '.$extraFieldInfo['id'].' OR field_id IS NULL)
-                            )';
+                                (field_id = ".$extraFieldInfo['id']." OR field_id IS NULL)
+                            )";
                     $extraFieldWasAdded = true;
                 }
             }
         }
 
-        $sql .= ' WHERE '.$filter_by_status_condition.' '.implode(' OR ', $where);
+        $sql .= " WHERE $filter_by_status_condition ".implode(' OR ', $where);
 
         if ($multiple_access_url) {
             $current_access_url_id = api_get_current_access_url_id();
@@ -1493,7 +1493,7 @@ class CourseManager
 
         if (isset($filterByActive)) {
             $filterByActive = (int) $filterByActive;
-            $sql .= ' AND user.active = '.$filterByActive;
+            $sql .= " AND user.active = $filterByActive";
         }
 
         if (!empty($searchByKeyword)) {
@@ -1505,7 +1505,7 @@ class CourseManager
                     ) ";
         }
 
-        $sql .= ' '.$order_by.' '.$limit;
+        $sql .= " $order_by $limit";
 
         $rs = Database::query($sql);
         $users = [];
@@ -1813,7 +1813,7 @@ class CourseManager
 
         // We get the coach for the given course in a given session.
         $sql = 'SELECT user_id FROM '.Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER).
-               ' WHERE session_id = '.$session_id.' AND c_id = '.$courseId.' AND status = 2';
+               " WHERE session_id = $session_id AND c_id = $courseId AND status = 2";
         $rs = Database::query($sql);
         while ($user = Database::fetch_array($rs)) {
             $userInfo = api_get_user_info($user['user_id']);
@@ -1876,7 +1876,7 @@ class CourseManager
                         FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." cu
                         INNER JOIN $userTable u
                         ON cu.user_id = u.user_id
-                        WHERE c_id = '$courseId' AND cu.status = ".STUDENT;
+                        WHERE c_id = $courseId AND cu.status = ".STUDENT;
 
                 if (!$includeInvitedUsers) {
                     $sql .= " AND u.status != ".INVITEE;
@@ -1919,7 +1919,7 @@ class CourseManager
             }
 
             if ($session_id != 0) {
-                $sql_query .= ' AND scu.session_id = '.$session_id;
+                $sql_query .= " AND scu.session_id = $session_id";
             }
 
             if (!$includeInvitedUsers) {
@@ -2897,7 +2897,7 @@ class CourseManager
                     ON (course.id = url.c_id) 
                     WHERE 
                         url.access_url_id = $urlId AND 
-                        cru.user_id = '$user_id' 
+                        cru.user_id = $user_id 
                         $withoutSpecialCourses
                         $languageCondition
                     ORDER BY course.title
@@ -3726,7 +3726,7 @@ class CourseManager
         // Step 1: We get all the categories of the user
         $table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
         $sql = "SELECT * FROM $table
-                WHERE user_id = '".$user_id."'
+                WHERE user_id = $user_id
                 ORDER BY sort ASC";
 
         $result = Database::query($sql);
@@ -3806,7 +3806,7 @@ class CourseManager
         $user_id = 0,
         $useUserLanguageFilterIfAvailable = true
     ) {
-        $user_id = $user_id ?: api_get_user_id();
+        $user_id = $user_id ? (int) $user_id : api_get_user_id();
         $user_category_id = (int) $user_category_id;
 
         // Table definitions
@@ -3848,7 +3848,7 @@ class CourseManager
                 INNER JOIN $TABLE_ACCESS_URL_REL_COURSE url
                 ON (url.c_id = course.id)
                 WHERE
-                    course_rel_user.user_id = '".$user_id."' AND
+                    course_rel_user.user_id = $user_id AND
                     $userCategoryCondition
                     $without_special_courses
                     $languageCondition
@@ -3856,7 +3856,7 @@ class CourseManager
         // If multiple URL access mode is enabled, only fetch courses
         // corresponding to the current URL.
         if (api_get_multiple_access_url() && $current_url_id != -1) {
-            $sql .= " AND access_url_id='".$current_url_id."'";
+            $sql .= " AND access_url_id = $current_url_id";
         }
         // Use user's classification for courses (if any).
         $sql .= " ORDER BY course_rel_user.user_course_cat, course_rel_user.sort ASC";
@@ -5074,10 +5074,10 @@ class CourseManager
         $visibilityCondition = '';
         $hidePrivate = api_get_setting('course_catalog_hide_private');
         if ($hidePrivate === 'true') {
-            $visibilityCondition .= ' AND '.$courseTableAlias.'.visibility <> '.COURSE_VISIBILITY_REGISTERED;
+            $visibilityCondition .= " AND $courseTableAlias.visibility <> ".COURSE_VISIBILITY_REGISTERED;
         }
         if ($hideClosed) {
-            $visibilityCondition .= ' AND '.$courseTableAlias.'.visibility NOT IN ('.COURSE_VISIBILITY_CLOSED.','.COURSE_VISIBILITY_HIDDEN.')';
+            $visibilityCondition .= " AND $courseTableAlias.visibility NOT IN (".COURSE_VISIBILITY_CLOSED.','.COURSE_VISIBILITY_HIDDEN.')';
         }
 
         // Check if course have users allowed to see it in the catalogue, then show only if current user is allowed to see it

+ 3 - 3
main/inc/lib/events.lib.php

@@ -463,6 +463,7 @@ class Event
         $exe_id = Database::escape_string($exe_id);
         $position = Database::escape_string($position);
         $now = api_get_utc_datetime();
+        $course_id = (int) $course_id;
 
         // check user_id or get from context
         if (empty($user_id)) {
@@ -473,10 +474,11 @@ class Event
             }
         }
         // check course_id or get from context
-        if (empty($course_id) or intval($course_id) != $course_id) {
+        if (empty($course_id)) {
             $course_id = api_get_course_int_id();
         }
         // check session_id or get from context
+        $session_id = (int) $session_id;
         if (empty($session_id)) {
             $session_id = api_get_session_id();
         }
@@ -508,8 +510,6 @@ class Event
             $answer = 0;
         }
 
-        $session_id = api_get_session_id();
-
         if (!empty($question_id) && !empty($exe_id) && !empty($user_id)) {
             if (is_null($answer)) {
                 $answer = '';

+ 53 - 6
main/inc/lib/exercise.lib.php

@@ -57,7 +57,7 @@ class ExerciseLib
         // Change false to true in the following line to enable answer hinting
         $debug_mark_answer = $show_answers;
         // Reads question information
-        if (!$objQuestionTmp = Question::read($questionId, $course_id)) {
+        if (!$objQuestionTmp = Question::read($questionId, $course)) {
             // Question not found
             return false;
         }
@@ -247,7 +247,7 @@ class ExerciseLib
                 if ($show_comment) {
                     $header .= Display::tag('th', get_lang('Feedback'));
                 }
-                $s .= '<table class="table table-hover">';
+                $s .= '<table class="table table-hover table-striped">';
                 $s .= Display::tag(
                     'tr',
                     $header,
@@ -432,7 +432,7 @@ class ExerciseLib
                     if ($exercise->feedback_type == EXERCISE_FEEDBACK_TYPE_END) {
                         $header .= Display::tag('th', get_lang('Feedback'));
                     }
-                    $s .= '<table class="table table-hover">';
+                    $s .= '<table class="table table-hover table-striped">';
                     $s .= Display::tag(
                         'tr',
                         $header,
@@ -2855,6 +2855,7 @@ HOTSPOT;
             $weight = float_format($weight, 1, $decimalSeparator, $thousandSeparator);
         }
 
+        $html = '';
         if ($show_percentage) {
             $percentageSign = '%';
             if ($hidePercentageSign) {
@@ -4560,7 +4561,7 @@ EOT;
         if (!empty($question_list)) {
             foreach ($question_list as $questionId) {
                 // Creates a temporary Question object
-                $objQuestionTmp = Question::read($questionId);
+                $objQuestionTmp = Question::read($questionId, $objExercise->course);
                 // This variable came from exercise_submit_modal.php
                 ob_start();
                 $choice = null;
@@ -5056,7 +5057,7 @@ EOT;
      * @return string
      */
     public static function getTotalScoreRibbon(
-        $objExercise,
+        Exercise $objExercise,
         $score,
         $weight,
         $checkPassPercentage = false,
@@ -5334,7 +5335,7 @@ EOT;
 
         $midnightTime = ChamiloApi::getServerMidnightTime();
 
-        $answeredQuestionsCount = ExerciseLib::countAnsweredQuestionsByUserAfterTime(
+        $answeredQuestionsCount = self::countAnsweredQuestionsByUserAfterTime(
             $midnightTime,
             $userId,
             $courseId,
@@ -5343,4 +5344,50 @@ EOT;
 
         return ($answeredQuestionsCount + $numberOfQuestions) > $questionsLimitPerDay;
     }
+
+    /**
+     * Check if an exercise complies with the requirements to be embedded in the mobile app or a video. By making sure
+     * it is set on one question per page and it only contains unique-answer or multiple-answer questions.
+     *
+     * @param array $exercise Exercise info
+     *
+     * @throws \Doctrine\ORM\Query\QueryException
+     *
+     * @return bool
+     */
+    public static function isQuizEmbeddable(array $exercise)
+    {
+        $em = Database::getManager();
+
+        if (2 != $exercise['type']) {
+            return false;
+        }
+
+        $countAll = $em
+            ->createQuery('SELECT COUNT(qq)
+                FROM ChamiloCourseBundle:CQuizQuestion qq
+                INNER JOIN ChamiloCourseBundle:CQuizRelQuestion qrq
+                   WITH qq.iid = qrq.questionId
+                WHERE qrq.exerciceId = :id'
+            )
+            ->setParameter('id', $exercise['iid'])
+            ->getSingleScalarResult();
+
+        $countOfAllowed = $em
+            ->createQuery('SELECT COUNT(qq)
+                FROM ChamiloCourseBundle:CQuizQuestion qq
+                INNER JOIN ChamiloCourseBundle:CQuizRelQuestion qrq
+                   WITH qq.iid = qrq.questionId
+                WHERE qrq.exerciceId = :id AND qq.type IN (:types)'
+            )
+            ->setParameters(
+                [
+                    'id' => $exercise['iid'],
+                    'types' => [UNIQUE_ANSWER, MULTIPLE_ANSWER, UNIQUE_ANSWER_IMAGE],
+                ]
+            )
+            ->getSingleScalarResult();
+
+        return $countAll === $countOfAllowed;
+    }
 }

+ 17 - 8
main/inc/lib/extra_field.lib.php

@@ -2119,20 +2119,29 @@ class ExtraField extends Model
     public function getJqgridActionLinks($token)
     {
         //With this function we can add actions to the jgrid (edit, delete, etc)
-        $editIcon = Display::return_icon('edit.png', get_lang('Edit'));
-        $deleteIcon = Display::return_icon('delete.png', get_lang('Delete'));
+        $editIcon = Display::return_icon('edit.png', get_lang('Edit'), '', ICON_SIZE_SMALL);
+        $deleteIcon = Display::return_icon('delete.png', get_lang('Delete'), '', ICON_SIZE_SMALL);
         $confirmMessage = addslashes(
             api_htmlentities(get_lang("ConfirmYourChoice"), ENT_QUOTES)
         );
 
-        return <<<JAVASCRIPT
-            function action_formatter(cellValue, options, rowObject) {
-                return '<a href="?action=edit&type={$this->type}&id=' + options.rowId + '">$editIcon</a>'
-                    + '<a \
+        $editButton = <<<JAVASCRIPT
+            <a href="?action=edit&type={$this->type}&id=' + options.rowId + '" class="btn btn-link btn-xs">\
+                $editIcon\
+            </a>
+JAVASCRIPT;
+        $deleteButton = <<<JAVASCRIPT
+            <a \
                     onclick="if (!confirm(\'$confirmMessage\')) {return false;}" \
-                    href="?sec_token=$token&type={$this->type}&id=' + options.rowId + '&action=delete">$deleteIcon</a>';
-            }
+                href="?sec_token=$token&type={$this->type}&id=' + options.rowId + '&action=delete" \
+                class="btn btn-link btn-xs">\
+                $deleteIcon\
+            </a>
 JAVASCRIPT;
+
+        return "function action_formatter(cellvalue, options, rowObject) {        
+            return '$editButton $deleteButton';
+        }";
     }
 
     /**

+ 2 - 4
main/inc/lib/formvalidator/FormValidator.class.php

@@ -142,7 +142,6 @@ EOT;
                 <div class="col-sm-8">
                     {icon}
                     {element}
-
                     <!-- BEGIN label_2 -->
                         <p class="help-block">{label_2}</p>
                     <!-- END label_2 -->
@@ -376,10 +375,9 @@ EOT;
      *
      * @param string $label Text appearing on the button
      * @param string $name Element name (for form treatment purposes)
-     * @param string $style
      * @param bool $createElement Whether to use the create or add method
-     *
-     * @param $attributes
+     * @param array $attributes
+
      * @return HTML_QuickForm_button
      */
     public function addButtonSave($label, $name = 'submit', $createElement = false, $attributes = [])

+ 126 - 86
main/inc/lib/myspace.lib.php

@@ -308,93 +308,139 @@ class MySpace
         return $return;
     }
 
+    /**
+     * Creates a small table in the last column of the table with the user overview.
+     *
+     * @param int   $user_id    the id of the user
+     *
+     * @return array List course
+     */
+
+    public static function returnCourseTracking($user_id)
+    {
+
+        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
+        // getting all the courses of the user
+        $sql = "SELECT * FROM $tbl_course_user
+                WHERE
+                    user_id = '" . intval($user_id) . "' AND
+                    relation_type<>" . COURSE_RELATION_TYPE_RRHH . " ";
+        $result = Database::query($sql);
+
+        $list = [];
+
+        while ($row = Database::fetch_array($result)) {
+            $courseInfo = api_get_course_info_by_id($row['c_id']);
+            $courseId = $courseInfo['real_id'];
+            $courseCode = $courseInfo['code'];
+
+            if (empty($courseInfo)) {
+                continue;
+            }
+
+            $avg_score = Tracking::get_avg_student_score($user_id, $courseCode);
+            if (is_numeric($avg_score)) {
+                $avg_score = round($avg_score, 2);
+            } else {
+                $avg_score = '-';
+            }
+
+            // student exercises results (obtained score, maximum score, number of exercises answered, score percentage)
+            $exercises_results = self::exercises_results($user_id, $courseCode);
+
+            $item = [
+                'code' => $courseInfo['code'],
+                'real_id' => $courseInfo['real_id'],
+                'title' => $courseInfo['title'],
+                'category' => $courseInfo['categoryName'],
+                'image_small' => $courseInfo['course_image'],
+                'image_large' => $courseInfo['course_image_large'],
+                'time_spent' => api_time_to_hms(Tracking::get_time_spent_on_the_course($user_id, $courseId)),
+                'student_progress' => round(Tracking::get_avg_student_progress($user_id, $courseCode)),
+                'student_score' => $avg_score,
+                'student_message' => Tracking::count_student_messages($user_id, $courseCode),
+                'student_assignments' => Tracking::count_student_assignments($user_id, $courseCode),
+                'student_exercises' => (is_null($exercises_results['percentage']) ? '' : $exercises_results['score_obtained'] . '/' . $exercises_results['score_possible'] . ' ( ' . $exercises_results['percentage'] . '% )'),
+                'questions_answered' => $exercises_results['questions_answered'],
+                'last_connection' => Tracking::get_last_connection_date_on_the_course($user_id, $courseInfo)
+            ];
+            $list[] = $item;
+        }
+
+        return $list;
+    }
+
     /**
      * Display a sortable table that contains an overview off all the
      * reporting progress of all users and all courses the user is subscribed to.
      *
      * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University, Belgium
+     *          Alex Aragon <alex.aragon@beeznest.com>, BeezNest, Perú
      *
-     * @version Dokeos 1.8.6
+     * @version Chamilo 1.11.8
      *
-     * @since October 2008
+     * @since April 2019
+     */
+
+    public static function returnTrackingUserOverviewFilter($user_id)
+    {
+
+        $tpl = new Template('', false, false, false, false, false, false);
+        $userInfo = api_get_user_info($user_id);
+
+        $avatar = UserManager::getUserPicture($user_id, USER_IMAGE_SIZE_SMALL);
+        $user = [
+            'id' => $user_id,
+            'code_user' => $userInfo['official_code'],
+            'complete_name' => $userInfo['complete_name'],
+            'username' => $userInfo['username'],
+            'course' => self::returnCourseTracking($user_id),
+            'avatar' => $avatar
+        ];
+
+        $tpl->assign('item', $user);
+        $templateName = $tpl->get_template('my_space/partials/tracking_user_overview.tpl');
+        $content = $tpl->fetch($templateName);
+
+        return $content;
+
+    }
+
+    /**
+     * Display a sortable table that contains an overview off all the
+     * reporting progress of all users and all courses the user is subscribed to.
+     *
+     * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University, Belgium
+     *         Alex Aragon <alex.aragon@beeznest.com>, BeezNest, Perú
+     *
+     * @version Chamilo 1.11.8
+     *
+     * @since October 2008, Update April 2019
      */
     public static function display_tracking_user_overview()
     {
         self::display_user_overview_export_options();
-        $t_head = '<table style="width: 100%;border:0;padding:0;border-collapse:collapse;table-layout: fixed">';
-        $t_head .= '<tr>';
-        $t_head .= '<th width="155px" style="border-left:0;border-bottom:0"><span>'.get_lang('Course').'</span></th>';
-        $t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('AvgTimeSpentInTheCourse'), 6, true).'</span></th>';
-        $t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('AvgStudentsProgress'), 6, true).'</span></th>';
-        $t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('AvgCourseScore'), 6, true).'</span></th>';
-        $t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('TotalNumberOfMessages'), 6, true).'</span></th>';
-        $t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('TotalNumberOfAssignments'), 6, true).'</span></th>';
-        $t_head .= '<th width="105px" style="border-bottom:0"><span>'.get_lang('TotalExercisesScoreObtained').'</span></th>';
-        $t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('TotalExercisesAnswered'), 6, true).'</span></th>';
-        $t_head .= '<th style="padding:0;border-bottom:0;border-right:0;"><span>'.get_lang('LatestLogin').'</span></th>';
-        $t_head .= '</tr></table>';
+
         $addparams = ['view' => 'admin', 'display' => 'user'];
         $table = new SortableTable(
             'tracking_user_overview',
             ['MySpace', 'get_number_of_users_tracking_overview'],
             ['MySpace', 'get_user_data_tracking_overview'],
-            0
-        );
-        $table->additional_parameters = $addparams;
-        $table->set_header(
             0,
-            get_lang('OfficialCode'),
-            true,
-            ['style' => 'font-size:8pt'],
-            ['style' => 'font-size:8pt']
-        );
-        if (api_is_western_name_order()) {
-            $table->set_header(
-                1,
-                get_lang('FirstName'),
-                true,
-                ['style' => 'font-size:8pt'],
-                ['style' => 'font-size:8pt']
-            );
-            $table->set_header(
-                2,
-                get_lang('LastName'),
-                true,
-                ['style' => 'font-size:8pt'],
-                ['style' => 'font-size:8pt']
-            );
-        } else {
-            $table->set_header(
-                1,
-                get_lang('LastName'),
-                true,
-                ['style' => 'font-size:8pt'],
-                ['style' => 'font-size:8pt']
-            );
-            $table->set_header(
-                2,
-                get_lang('FirstName'),
-                true,
-                ['style' => 'font-size:8pt'],
-                ['style' => 'font-size:8pt']
-            );
-        }
-        $table->set_header(
-            3,
-            get_lang('LoginName'),
-            true,
-            ['style' => 'font-size:8pt'],
-            ['style' => 'font-size:8pt']
-        );
-        $table->set_header(
-            4,
-            $t_head,
-            false,
-            ['style' => 'width:90%;border:0;padding:0;font-size:7.5pt;'],
-            ['style' => 'width:90%;padding:0;font-size:7.5pt;']
+            20,
+            'ASC',
+            null, [
+                'class' => 'table table-transparent'
+            ]
         );
-        $table->set_column_filter(4, ['MySpace', 'course_info_tracking_filter']);
-        $table->display();
+        $table->additional_parameters = $addparams;
+
+        $table->set_column_filter(0, ['MySpace', 'returnTrackingUserOverviewFilter']);
+        $tableContent = $table->return_table();
+        $tpl = new Template('', false, false, false, false, false, false);
+        $tpl->assign('table', $tableContent);
+        $templateName = $tpl->get_template('my_space/user_summary.tpl');
+        $tpl->display($templateName);
     }
 
     /**
@@ -2163,11 +2209,13 @@ class MySpace
     public static function get_user_data_tracking_overview($from, $numberItems, $column, $direction)
     {
         $isWestern = api_is_western_name_order();
+
         switch ($column) {
             case '0':
-                $column = 'official_code';
+                $column = $isWestern ? 'firstname' : 'lastname';
+                //$column = 'official_code';
                 break;
-            case '1':
+            /*case '1':
                 $column = $isWestern ? 'firstname' : 'lastname';
                 break;
             case '2':
@@ -2178,7 +2226,7 @@ class MySpace
                 break;
             case '4':
                 $column = 'username';
-                break;
+                break;*/
         }
 
         $order = [
@@ -2187,23 +2235,15 @@ class MySpace
         $userList = UserManager::get_user_list([], $order, $from, $numberItems);
         $return = [];
         foreach ($userList as $user) {
-            $firstPosition = $user['lastname'];
-            $secondPosition = $user['firstname'];
-            if ($isWestern) {
+            //$firstPosition = $user['lastname'];
+            //$secondPosition = $user['firstname'];
+            /*if ($isWestern) {
                 $firstPosition = $user['firstname'];
                 $secondPosition = $user['lastname'];
-            }
+            }*/
             $return[] = [
-                '0' => $user['official_code'],
-                'col0' => $user['official_code'],
-                '1' => $firstPosition,
-                'col1' => $firstPosition,
-                '2' => $secondPosition,
-                'col2' => $secondPosition,
-                '3' => $user['username'],
-                'col3' => $user['username'],
-                '4' => $user['user_id'],
-                'col4' => $user['user_id'],
+                '0' => $user['user_id'],
+                'col0' => $user['user_id']
             ];
         }
 

+ 26 - 31
main/inc/lib/sessionmanager.lib.php

@@ -148,9 +148,7 @@ class SessionManager
      * @return mixed Session ID on success, error message otherwise
      *
      * @todo use an array to replace all this parameters or use the model.lib.php ...
-     *
-     * @return mixed Session ID on success, error message otherwise
-     * */
+     */
     public static function create_session(
         $name,
         $startDate,
@@ -3093,53 +3091,50 @@ class SessionManager
     /**
      * Delete sessions categories.
      *
-     * @author Jhon Hinojosa <jhon.hinojosa@dokeos.com>, from existing code
-     *
-     * @param    array    id_checked
-     * @param    bool    include delete session
-     * @param    bool    optional, true if the function is called by a webservice, false otherwise
+     * @param array|int $categoryId
+     * @param bool      $deleteSessions Optional. Include delete session.
+     * @param bool      $fromWs         Optional. True if the function is called by a webservice, false otherwise.
      *
      * @return bool Nothing, or false on error
      *              The parameters is a array to delete sessions
-     * */
-    public static function delete_session_category($id_checked, $delete_session = false, $from_ws = false)
+     *
+     * @author Jhon Hinojosa <jhon.hinojosa@dokeos.com>, from existing code
+     */
+    public static function delete_session_category($categoryId, $deleteSessions = false, $fromWs = false)
     {
-        $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
-        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
-        if (is_array($id_checked)) {
-            $id_checked = Database::escape_string(implode(',', $id_checked));
+        $tblSessionCategory = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
+        $tblSession = Database::get_main_table(TABLE_MAIN_SESSION);
+
+        if (is_array($categoryId)) {
+            $categoryId = array_map('intval', $categoryId);
         } else {
-            $id_checked = intval($id_checked);
+            $categoryId = [(int) $categoryId];
         }
 
+        $categoryId = implode(', ', $categoryId);
         //Setting session_category_id to 0
-        $sql = "UPDATE $tbl_session SET session_category_id = NULL
-                WHERE session_category_id IN (".$id_checked.")";
-        Database::query($sql);
-
-        $sql = "SELECT id FROM $tbl_session WHERE session_category_id IN (".$id_checked.")";
+        if ($deleteSessions) {
+            $sql = "SELECT id FROM $tblSession WHERE session_category_id IN ($categoryId)";
         $result = Database::query($sql);
         while ($rows = Database::fetch_array($result)) {
-            $session_id = $rows['id'];
-            if ($delete_session) {
-                if ($from_ws) {
-                    self::delete($session_id, true);
+                $sessionId = $rows['id'];
+                self::delete($sessionId, $fromWs);
+            }
                 } else {
-                    self::delete($session_id);
+            $sql = "UPDATE $tblSession SET session_category_id = NULL WHERE session_category_id IN ($categoryId)";
+            Database::query($sql);
                 }
-            }
-        }
-        $sql = "DELETE FROM $tbl_session_category WHERE id IN (".$id_checked.")";
+
+        $sql = "DELETE FROM $tblSessionCategory WHERE id IN ($categoryId)";
         Database::query($sql);
 
         // Add event to system log
-        $user_id = api_get_user_id();
         Event::addEvent(
             LOG_SESSION_CATEGORY_DELETE,
             LOG_SESSION_CATEGORY_ID,
-            $id_checked,
+            $categoryId,
             api_get_utc_datetime(),
-            $user_id
+            api_get_user_id()
         );
 
         return true;

+ 113 - 36
main/inc/lib/social.lib.php

@@ -815,23 +815,16 @@ class SocialManager extends UserManager
      *                         friends,
      *                         groups search
      * @param int    $group_id
-     * @param int    $userId
+     * @param int    $user_id
      */
-    public static function getAvatarBlock($show = '', $group_id = 0, $userId = 0)
+    public static function show_social_avatar_block($show = '', $group_id = 0, $user_id = 0)
     {
-        if (empty($userId)) {
-            $userId = api_get_user_id();
-        }
-
-        $userInfo = api_get_user_info($userId, true, false, true, true);
+        $user_id = (int) $user_id;
+        $group_id = (int) $group_id;
 
-        $user = [
-            'id' => $userInfo['user_id'],
-            'complete_name' => $userInfo['complete_name'],
-            'username' => $userInfo['username'],
-            'email' => $userInfo['email'],
-            'status' => $userInfo['status'],
-        ];
+        if (empty($user_id)) {
+            $user_id = api_get_user_id();
+        }
 
         $show_groups = [
             'groups',
@@ -851,46 +844,44 @@ class SocialManager extends UserManager
         if (in_array($show, $show_groups) && !empty($group_id)) {
             // Group image
             $userGroup = new UserGroup();
-            $groupInfo = $userGroup->get($group_id);
+            $group_info = $userGroup->get($group_id);
 
-            $userGroupImage = [
-                'big' => $userGroup->get_picture_group(
+            $userGroupImage = $userGroup->get_picture_group(
                     $group_id,
-                    $groupInfo['picture'],
+                $group_info['picture'],
                     128,
                     GROUP_IMAGE_SIZE_BIG
-                ),
-                'normal' => $userGroup->get_picture_group(
-                    $group_id,
-                    $groupInfo['picture'],
-                    128,
-                    GROUP_IMAGE_SIZE_MEDIUM
-                ),
-            ];
+            );
 
             $template->assign('show_group', true);
-            $template->assign('group', $groupInfo);
-            $template->assign('avatar', $userGroupImage);
+            $template->assign('group_id', $group_id);
+            $template->assign('user_group_image', $userGroupImage);
+            $template->assign(
+                'user_is_group_admin',
+                $userGroup->is_group_admin(
+                    $group_id,
+                    api_get_user_id()
+                )
+            );
         } else {
             $template->assign('show_group', false);
             $template->assign('show_user', true);
-            $template->assign('user', $user);
             $template->assign(
-                'avatar',
+                'user_image',
                 [
                     'big' => UserManager::getUserPicture(
-                        $userId,
+                        $user_id,
                         USER_IMAGE_SIZE_BIG
                     ),
                     'normal' => UserManager::getUserPicture(
-                        $userId,
+                        $user_id,
                         USER_IMAGE_SIZE_MEDIUM
                     ),
                 ]
             );
         }
 
-        $skillBlock = $template->get_template('social/avatar_block.html.twig');
+        $skillBlock = $template->get_template('social/avatar_block.tpl');
 
         return $template->fetch($skillBlock);
     }
@@ -1443,6 +1434,14 @@ class SocialManager extends UserManager
                 $links .= '</ul>';
             }
 
+            $html .= Display::panelCollapse(
+                get_lang('SocialNetwork'),
+                $links,
+                'social-network-menu',
+                null,
+                'sn-sidebar',
+                'sn-sidebar-collapse'
+            );
         }
 
         if (in_array($show, $show_groups) && !empty($group_id)) {
@@ -1580,7 +1579,14 @@ class SocialManager extends UserManager
             }
 
             $links .= '</ul>';
-
+            $html .= Display::panelCollapse(
+                get_lang('SocialNetwork'),
+                $links,
+                'social-network-menu',
+                null,
+                'sn-sidebar',
+                'sn-sidebar-collapse'
+            );
 
             if ($show_full_profile && $user_id == intval(api_get_user_id())) {
                 $personal_course_list = UserManager::get_personal_session_course_list($user_id);
@@ -1733,6 +1739,70 @@ class SocialManager extends UserManager
         return $html;
     }
 
+    /**
+     * Displays the information of an individual user.
+     *
+     * @param int $user_id
+     *
+     * @return string
+     */
+    public static function display_individual_user($user_id)
+    {
+        global $interbreadcrumb;
+        $safe_user_id = (int) $user_id;
+        $currentUserId = api_get_user_id();
+
+        $user_table = Database::get_main_table(TABLE_MAIN_USER);
+        $sql = "SELECT * FROM $user_table WHERE user_id = ".$safe_user_id;
+        $result = Database::query($sql);
+        $html = null;
+        if (Database::num_rows($result) == 1) {
+            $user_object = Database::fetch_object($result);
+            $userInfo = api_get_user_info($user_id);
+            $alt = $userInfo['complete_name'].($currentUserId == $user_id ? '&nbsp;('.get_lang('Me').')' : '');
+            $status = get_status_from_code($user_object->status);
+            $interbreadcrumb[] = ['url' => 'whoisonline.php', 'name' => get_lang('UsersOnLineList')];
+
+            $html .= '<div class ="thumbnail">';
+            $fullurl = $userInfo['avatar'];
+
+            $html .= '<img src="'.$fullurl.'" alt="'.$alt.'" />';
+
+            if (!empty($status)) {
+                $html .= '<div class="caption">'.$status.'</div>';
+            }
+            $html .= '</div>';
+
+            if (api_get_setting('show_email_addresses') == 'true') {
+                $html .= Display::encrypted_mailto_link($user_object->email, $user_object->email).'<br />';
+            }
+
+            if ($user_object->competences) {
+                $html .= Display::page_subheader(get_lang('MyCompetences'));
+                $html .= '<p>'.$user_object->competences.'</p>';
+            }
+            if ($user_object->diplomas) {
+                $html .= Display::page_subheader(get_lang('MyDiplomas'));
+                $html .= '<p>'.$user_object->diplomas.'</p>';
+            }
+            if ($user_object->teach) {
+                $html .= Display::page_subheader(get_lang('MyTeach'));
+                $html .= '<p>'.$user_object->teach.'</p>';
+            }
+            self::display_productions($user_object->user_id);
+            if ($user_object->openarea) {
+                $html .= Display::page_subheader(get_lang('MyPersonalOpenArea'));
+                $html .= '<p>'.$user_object->openarea.'</p>';
+            }
+        } else {
+            $html .= '<div class="actions-title">';
+            $html .= get_lang('UsersOnLineList');
+            $html .= '</div>';
+        }
+
+        return $html;
+    }
+
     /**
      * Display productions in who is online.
      *
@@ -2440,7 +2510,7 @@ class SocialManager extends UserManager
         $userId = (int) $userId;
         $userRelationType = 0;
 
-        $socialAvatarBlock = self::getAvatarBlock(
+        $socialAvatarBlock = self::show_social_avatar_block(
             $groupBlock,
             $groupId,
             $userId
@@ -2534,6 +2604,14 @@ class SocialManager extends UserManager
         $template->assign('user_relation', $userRelationType);
         $template->assign('user_relation_type_friend', USER_RELATION_TYPE_FRIEND);
         $template->assign('show_full_profile', $show_full_profile);
+
+        $templateName = $template->get_template('social/user_block.tpl');
+
+        if (in_array($groupBlock, ['groups', 'group_edit', 'member_list'])) {
+            $templateName = $template->get_template('social/group_block.tpl');
+        }
+
+        $template->assign('social_avatar_block', $template->fetch($templateName));
     }
 
     /**
@@ -3534,7 +3612,6 @@ class SocialManager extends UserManager
         $urlImg = api_get_path(WEB_IMG_PATH);
         $isAdmin = self::is_admin($authorId);
 
-
         if ($userStatus === 5) {
             if ($authorInfo['has_certificates']) {
                 $iconStatus = Display::return_icon('identifier_graduated.png',get_lang('User status'),['class' => 'float-left'],ICON_SIZE_SMALL);

+ 7 - 3
main/inc/lib/sortable_table.class.php

@@ -116,7 +116,7 @@ class SortableTable extends HTML_Table
      * @param string $default_order_direction   The default order direction;
      *                                          either the constant 'ASC' or 'DESC'
      * @param string $table_id
-     * @param string $style
+     * @param array $parameters         They are custom attributes of the table
      */
     public function __construct(
         $table_name = 'table',
@@ -126,13 +126,17 @@ class SortableTable extends HTML_Table
         $default_items_per_page = 20,
         $default_order_direction = 'ASC',
         $table_id = null,
-        $style = null
+        $parameters = []
     ) {
         if (empty($table_id)) {
             $table_id = $table_name.uniqid();
         }
+        if(isset($parameters) && empty($parameters)){
+            $parameters = ['class' => 'table table-bordered data_table', 'id' => $table_id];
+        }
+
         $this->table_id = $table_id;
-        parent::__construct(['class' => 'table table-hover mt-4 mb-4'.$style, 'id' => $table_id]);
+        parent::__construct($parameters);
         $this->table_name = $table_name;
         $this->additional_parameters = [];
         $this->param_prefix = $table_name.'_';

+ 0 - 3
main/inc/lib/system_announcements.lib.php

@@ -685,9 +685,6 @@ class SystemAnnouncementManager
             ->setContent($content)
             ->setDateStart($dateStart)
             ->setDateEnd($dateEnd)
-            /*->setVisibleTeacher($visible_teacher)
-            ->setVisibleStudent($visible_student)
-            ->setVisibleGuest($visible_guest)*/
             ->setAccessUrlId(api_get_current_access_url_id());
 
         $em->merge($announcement);

+ 9 - 0
main/inc/lib/text.lib.php

@@ -73,12 +73,21 @@ function api_set_encoding_html(&$string, $encoding)
         }
     } else {
         $count = 1;
+        if (strpos('</head>', strtolower($string)) !== false) {
         $string = str_ireplace(
             '</head>',
             '<meta http-equiv="Content-Type" content="text/html; charset='.$encoding.'"/></head>',
             $string,
             $count
         );
+        } else {
+            $string = str_ireplace(
+                '<body>',
+                '<head><meta http-equiv="Content-Type" content="text/html; charset='.$encoding.'"/></head><body>',
+                $string,
+                $count
+            );
+        }
     }
     $string = api_convert_encoding($string, $encoding, $old_encoding);
 }

+ 3 - 2
main/inc/lib/timeline.lib.php

@@ -219,15 +219,16 @@ class Timeline extends Model
 
     /**
      * @param array $params
+     * @param bool  $showQuery
      *
      * @return bool
      */
-    public function save($params)
+    public function save($params, $showQuery = false)
     {
         $params['c_id'] = api_get_course_int_id();
         $params['parent_id'] = '0';
         $params['type'] = 'default';
-        $id = parent::save($params);
+        $id = parent::save($params, $showQuery);
         if (!empty($id)) {
             //event_system(LOG_CAREER_CREATE, LOG_CAREER_ID, $id, api_get_utc_datetime(), api_get_user_id());
         }

+ 42 - 48
main/inc/lib/tracking.lib.php

@@ -1550,12 +1550,12 @@ class Tracking
     /**
      * Calculates the time spent on the platform by a user.
      *
-     * @param   int|array User id
+     * @param int|array $userId
      * @param string $timeFilter type of time filter: 'last_week' or 'custom'
      * @param string $start_date start date date('Y-m-d H:i:s')
      * @param string $end_date   end date date('Y-m-d H:i:s')
      *
-     * @return int $nb_seconds
+     * @return int
      */
     public static function get_time_spent_on_the_platform(
         $userId,
@@ -1608,9 +1608,9 @@ class Tracking
 
         if ($diff >= 0) {
             return $diff;
-        } else {
-            return -1;
         }
+
+        return -1;
     }
 
     /**
@@ -1796,7 +1796,7 @@ class Tracking
         if (empty($studentList)) {
             return 0;
         }
-        $days = intval($days);
+        $days = (int) $days;
         $date = api_get_utc_datetime(strtotime($days.' days ago'));
         $studentList = array_map('intval', $studentList);
 
@@ -1925,17 +1925,17 @@ class Tracking
                 // fin de acceso a la sesión
                 $sessionInfo = SessionManager::fetch($session_id);
                 $last_access = $sessionInfo['access_end_date'];
-                $where_condition = ' AND access_date < "'.$last_access.'" ';
+                $where_condition = ' AND logout_course_date < "'.$last_access.'" ';
             }
 
-            $tbl_track_e_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
-            $sql = 'SELECT access_date
-                FROM '.$tbl_track_e_access.'
-                WHERE   access_user_id = '.$student_id.' AND
-                        c_id = "'.$courseId.'" AND
-                        access_session_id = '.$session_id.$where_condition.'
-                ORDER BY access_date DESC
-                LIMIT 0,1';
+            $tbl_track_e_course_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
+            $sql = "SELECT logout_course_date
+                FROM $tbl_track_e_course_access
+                WHERE   user_id = $student_id AND
+                        c_id = $courseId AND
+                        session_id = $session_id $where_condition
+                ORDER BY logout_course_date DESC
+                LIMIT 0,1";
 
             $rs = Database::query($sql);
             if (Database::num_rows($rs) > 0) {
@@ -1972,14 +1972,14 @@ class Tracking
                 }
             }
         } else {
-            $tbl_track_e_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
-            $sql = 'SELECT access_date
-                    FROM '.$tbl_track_e_access.'
-                    WHERE   access_user_id = '.$student_id.' AND
-                            c_id = "'.$courseId.'" AND
-                            access_session_id = '.$session_id.'
-                    ORDER BY access_date DESC
-                    LIMIT 0,1';
+            $tbl_track_e_course_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
+            $sql = "SELECT logout_course_date
+                    FROM $tbl_track_e_course_access
+                    WHERE   user_id = $student_id AND
+                            c_id = $courseId AND
+                            session_id = $session_id
+                    ORDER BY logout_course_date DESC
+                    LIMIT 0,1";
 
             $rs = Database::query($sql);
             if (Database::num_rows($rs) > 0) {
@@ -2042,7 +2042,7 @@ class Tracking
         if ($start < 0) {
             $start = 0;
         }
-        if (!isset($stop) or ($stop < 0)) {
+        if (!isset($stop) || $stop < 0) {
             $stop = api_get_utc_datetime();
         }
 
@@ -2053,8 +2053,8 @@ class Tracking
         $roundedStart = Database::escape_string($roundedStart);
         $roundedStop = Database::escape_string($roundedStop);
         $month_filter = " AND login_course_date > '$roundedStart' AND login_course_date < '$roundedStop' ";
-        $courseId = intval($courseId);
-        $session_id = intval($session_id);
+        $courseId = (int) $courseId;
+        $session_id = (int) $session_id;
         $count = 0;
         $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
         $sql = "SELECT count(*) as count_connections
@@ -2106,7 +2106,7 @@ class Tracking
      */
     public static function count_course_per_student($user_id, $include_sessions = true)
     {
-        $user_id = intval($user_id);
+        $user_id = (int) $user_id;
         $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
         $tbl_session_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
 
@@ -2363,9 +2363,9 @@ class Tracking
         $courseId,
         $session_id
     ) {
-        $courseId = intval($courseId);
-        $user_id = intval($user_id);
-        $session_id = intval($session_id);
+        $courseId = (int) $courseId;
+        $user_id = (int) $user_id;
+        $session_id = (int) $session_id;
 
         if (empty($exercise_list)) {
             return '0%';
@@ -2636,7 +2636,7 @@ class Tracking
             return false;
         }
 
-        $sessionId = intval($sessionId);
+        $sessionId = (int) $sessionId;
         $courseInfo = api_get_course_info($courseCode);
 
         if (empty($courseInfo)) {
@@ -2688,7 +2688,7 @@ class Tracking
             $studentId = array_map('intval', $studentId);
             $conditions[] = " lp_view.user_id IN (".implode(',', $studentId).")  ";
         } else {
-            $studentId = intval($studentId);
+            $studentId = (int) $studentId;
             $conditions[] = " lp_view.user_id = '$studentId' ";
 
             if (empty($lpIdList)) {
@@ -3185,13 +3185,11 @@ class Tracking
      * 3. And finally it will return the average between 1. and 2.
      * This function does not take the results of a Test out of a LP.
      *
-     * @param   int|array   Array of user ids or an user id
+     * @param int|array $student_id  Array of user ids or an user id
      * @param string $course_code Course code
      * @param array  $lp_ids      List of LP ids
      * @param int    $session_id  Session id (optional), if param $session_id is 0(default)
      *                            it'll return results including sessions, 0 = session is not filtered
-     * @param   bool        Returns an array of the type [sum_score, num_score] if set to true
-     * @param   bool        get only the latest attempts or ALL attempts
      *
      * @return string value (number %) Which represents a round integer explain in got in 3
      */
@@ -3220,11 +3218,11 @@ class Tracking
 
         // Compose a filter based on optional learning paths list given
         if (!empty($lp_ids) && count($lp_ids) > 0) {
-            $conditions[] = " id IN(".implode(',', $lp_ids).") ";
+            $conditions[] = ' id IN ('.implode(',', $lp_ids).') ';
         }
 
         // Compose a filter based on optional session id
-        $session_id = intval($session_id);
+        $session_id = (int) $session_id;
         if (!empty($session_id)) {
             $conditions[] = " session_id = $session_id ";
         }
@@ -3233,6 +3231,7 @@ class Tracking
             array_walk($student_id, 'intval');
             $conditions[] = " lp_view.user_id IN (".implode(',', $student_id).") ";
         } else {
+            $student_id = (int) $student_id;
             $conditions[] = " lp_view.user_id = $student_id ";
         }
 
@@ -3242,11 +3241,11 @@ class Tracking
                     SUM(lp_i.max_score) sum_max_score
                 FROM $lp_table as lp
                 INNER JOIN $lp_item_table as lp_i
-                ON lp.id = lp_id AND lp.c_id = lp_i.c_id
+                ON lp.iid = lp_id AND lp.c_id = lp_i.c_id
                 INNER JOIN $lp_view_table as lp_view
                 ON lp_view.lp_id = lp_i.lp_id AND lp_view.c_id = lp_i.c_id
                 INNER JOIN $lp_item_view_table as lp_iv
-                ON lp_i.id = lp_iv.lp_item_id AND lp_view.c_id = lp_iv.c_id AND lp_iv.lp_view_id = lp_view.id
+                ON lp_i.iid = lp_iv.lp_item_id AND lp_view.c_id = lp_iv.c_id AND lp_iv.lp_view_id = lp_view.iid
                 WHERE (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."') AND
                 $conditionsToString
         ";
@@ -3994,17 +3993,12 @@ class Tracking
     /**
      * Count assignments per student.
      *
-     * @param $student_id
-     * @param null $course_code
-     * @param null $session_id
+     * @param array|int $student_id
+     * @param string    $course_code
+     * @param int       $session_id  if param is null(default) return count of assignments including sessions,
+     *                               0 = session is not filtered
      *
      * @return int Count of assignments
-     *
-     * @internal param array|int $Student id(s)
-     * @internal param Course $string code
-     * @internal param Session $int id (optional),
-     * if param $session_id is null(default) return count of assignments
-     * including sessions, 0 = session is not filtered
      */
     public static function count_student_assignments(
         $student_id,
@@ -4032,7 +4026,7 @@ class Tracking
             $studentList = array_map('intval', $student_id);
             $conditions[] = " ip.insert_user_id IN ('".implode("','", $studentList)."') ";
         } else {
-            $student_id = intval($student_id);
+            $student_id = (int) $student_id;
             $conditions[] = " ip.insert_user_id = '$student_id' ";
         }
 

+ 20 - 10
main/inc/lib/usermanager.lib.php

@@ -4472,7 +4472,7 @@ class UserManager
      *
      * @return string HTML form
      */
-    public static function getSearchForm($query, $defaultParams = [])
+    public static function get_search_form($query, $defaultParams = [])
     {
         $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
         $form = new FormValidator(
@@ -4481,12 +4481,23 @@ class UserManager
             api_get_path(WEB_PATH).'main/social/search.php',
             '',
             [],
-            'box-no-label'
+            FormValidator::LAYOUT_HORIZONTAL
         );
 
-        $form->addText('q', get_lang('UsersGroups'), false, [
-            "id" => "q",
-        ]);
+        $query = Security::remove_XSS($query);
+
+        if (!empty($query)) {
+            $form->addHeader(get_lang('Results').' "'.$query.'"');
+        }
+
+        $form->addText(
+            'q',
+            get_lang('UsersGroups'),
+            false,
+            [
+                'id' => 'q',
+            ]
+        );
         $options = [
             0 => get_lang('Select'),
             1 => get_lang('User'),
@@ -4522,8 +4533,8 @@ class UserManager
             }
         }
 
-        $defaults['search_type'] = intval($searchType);
-        $defaults['q'] = api_htmlentities(Security::remove_XSS($query));
+        $defaults['search_type'] = (int) $searchType;
+        $defaults['q'] = $query;
 
         if (!empty($defaultParams)) {
             $defaults = array_merge($defaults, $defaultParams);
@@ -4628,8 +4639,8 @@ class UserManager
     /**
      * Deletes a contact.
      *
-     * @param int user friend id
-     * @param bool true will delete ALL friends relationship from $friend_id
+     * @param bool   $friend_id
+     * @param bool   $real_removed          true will delete ALL friends relationship
      * @param string                                              $with_status_condition
      *
      * @author isaac flores paz <isaac.flores@dokeos.com>
@@ -6443,7 +6454,6 @@ SQL;
         return (int) $row['count'];
     }
 
-    
     /**
      * @param array $userInfo
      * @param int   $searchYear

+ 16 - 28
main/inc/lib/userportal.lib.php

@@ -662,20 +662,8 @@ class IndexManager
     ) {
         $html = '';
         if (!empty($idAccordion)) {
-            /*$html .= '<div class="panel-group" id="'.$idAccordion.'" role="tablist" aria-multiselectable="true">';
-            $html .= '<div class="panel panel-default" id="'.$id.'">';
-            $html .= '<div class="panel-heading" role="tab"><h4 class="panel-title">';
-            $html .= '<a role="button" data-toggle="collapse" data-parent="#'.$idAccordion.'" href="#'.$idCollapse.'" aria-expanded="true" aria-controls="'.$idCollapse.'">'.$title.'</a>';
-            $html .= '</h4></div>';
-            $html .= '<div id="'.$idCollapse.'" class="panel-collapse collapse in" role="tabpanel">';
-            $html .= '<div class="panel-body">'.$content.'</div>';
-            $html .= '</div></div></div>';*/
-
             $html = Display::panel($content, $title);
         } else {
-            /*if (!empty($id)) {
-                $params['id'] = $id;
-            }        */
             $html = Display::panel($content, $title);
         }
 
@@ -843,24 +831,24 @@ class IndexManager
                     'link' => api_get_path(WEB_PATH).'main/social/invitations.php',
                     'title' => get_lang('PendingInvitations').$total_invitations,
                 ];
-            } else {
-                $items[] = [
-                    'class' => 'personal-data',
-                    'icon' => Display::return_icon('database.png', get_lang('PersonalDataReport')),
-                    'link' => api_get_path(WEB_CODE_PATH).'social/personal_data.php',
-                    'title' => get_lang('PersonalDataReport'),
-                ];
             }
+        }
 
-            if (api_get_configuration_value('allow_my_files_link_in_homepage')) {
-                if (api_get_setting('allow_my_files') !== 'false') {
-                    $items[] = [
-                        'class' => 'myfiles-social',
-                        'icon' => Display::return_icon('sn-files.png', get_lang('Files')),
-                        'link' => api_get_path(WEB_PATH).'main/social/myfiles.php',
-                        'title' => get_lang('MyFiles'),
-                    ];
-                }
+        $items[] = [
+            'class' => 'personal-data',
+            'icon' => Display::return_icon('database.png', get_lang('PersonalDataReport')),
+            'link' => api_get_path(WEB_CODE_PATH).'social/personal_data.php',
+            'title' => get_lang('PersonalDataReport'),
+        ];
+
+        if (api_get_configuration_value('allow_my_files_link_in_homepage')) {
+            if (api_get_setting('allow_my_files') !== 'false') {
+                $items[] = [
+                    'class' => 'myfiles-social',
+                    'icon' => Display::return_icon('sn-files.png', get_lang('Files')),
+                    'link' => api_get_path(WEB_PATH).'main/social/myfiles.php',
+                    'title' => get_lang('MyFiles'),
+                ];
             }
         }
 

+ 11 - 9
main/link/link_goto.php

@@ -19,15 +19,17 @@
 require_once __DIR__.'/../inc/global.inc.php';
 $this_section = SECTION_COURSES;
 
-$linkId = intval($_GET['link_id']);
+$linkId = isset($_GET['link_id']) ? $_GET['link_id'] : 0;
 
 $linkInfo = Link::getLinkInfo($linkId);
-$linkUrl = html_entity_decode(Security::remove_XSS($linkInfo['url']));
-// Launch event
-Event::event_link($linkId);
+if ($linkInfo) {
+    $linkUrl = html_entity_decode(Security::remove_XSS($linkInfo['url']));
+    // Launch event
+    Event::event_link($linkId);
 
-header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1
-header("Cache-Control: post-check=0, pre-check=0", false);
-header("Pragma: no-cache"); // HTTP/1.0
-header("Location: $linkUrl");
-exit;
+    header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1
+    header("Cache-Control: post-check=0, pre-check=0", false);
+    header("Pragma: no-cache"); // HTTP/1.0
+    header("Location: $linkUrl");
+    exit;
+}

+ 6 - 1
main/messages/download.php

@@ -13,12 +13,17 @@ session_cache_limiter('public');
 
 require_once __DIR__.'/../inc/global.inc.php';
 
+$file_url = isset($_GET['file']) ? $_GET['file'] : '';
+
+if (empty($file_url)) {
+    api_not_allowed();
+}
+
 // IMPORTANT to avoid caching of documents
 header('Expires: Wed, 01 Jan 1990 00:00:00 GMT');
 header('Cache-Control: public');
 header('Pragma: no-cache');
 
-$file_url = $_GET['file'];
 //change the '&' that got rewritten to '///' by mod_rewrite back to '&'
 $file_url = str_replace('///', '&', $file_url);
 //still a space present? it must be a '+' (that got replaced by mod_rewrite)

+ 10 - 1
main/messages/inbox.php

@@ -14,6 +14,15 @@ if (api_get_setting('allow_message_tool') != 'true') {
     api_not_allowed(true);
 }
 
+$logInfo = [
+    'tool' => 'Messages',
+    'tool_id' => 0,
+    'tool_id_detail' => 0,
+    'action' => isset($_GET['action']) ? $_GET['action'] : 'inbox',
+    'action_details' => '',
+];
+Event::registerLog($logInfo);
+
 $allowSocial = api_get_setting('allow_social_tool') == 'true';
 $allowMessage = api_get_setting('allow_message_tool') == 'true';
 
@@ -172,4 +181,4 @@ $social_layout = $tpl->get_template('message/inbox.html.twig');
 $content = $tpl->fetch($social_layout);
 $tpl->assign('message', $show_message);
 $tpl->assign('content', $content);
-$tpl->display_one_col_template();
+$tpl->display_one_col_template();

+ 13 - 5
main/messages/new_message.php

@@ -22,6 +22,15 @@ if (api_get_setting('allow_message_tool') !== 'true') {
     api_not_allowed(true);
 }
 
+$logInfo = [
+    'tool' => 'Messages',
+    'tool_id' => 0,
+    'tool_id_detail' => 0,
+    'action' => 'new_message',
+    'action_details' => isset($_GET['re_id']) ? 're_id' : '',
+];
+Event::registerLog($logInfo);
+
 $allowSocial = api_get_setting('allow_social_tool') == 'true';
 $nameTools = api_xml_http_response_encode(get_lang('Messages'));
 
@@ -49,7 +58,6 @@ function add_image_form() {
     }
 }
 </script>';
-
 $nameTools = get_lang('ComposeMessage');
 
 $tpl = new Template($nameTools);
@@ -176,7 +184,7 @@ function manageForm($default, $select_from_user_list = null, $sent_to = '', $tpl
 
     if (isset($_GET['re_id'])) {
         $message_reply_info = MessageManager::get_message_by_id($_GET['re_id']);
-        $default['title'] = get_lang('MailSubjectReplyShort').' '.$message_reply_info['title'];
+        $default['title'] = get_lang('MailSubjectReplyShort').' '.Security::remove_XSS($message_reply_info['title']);
         $form->addHidden('re_id', (int) $_GET['re_id']);
         $form->addHidden('save_form', 'save_form');
 
@@ -197,14 +205,14 @@ function manageForm($default, $select_from_user_list = null, $sent_to = '', $tpl
             $fileListToString = !empty($attachments) ? implode('<br />', $attachments) : '';
             $form->addLabel('', $fileListToString);
         }
-        $default['title'] = '['.get_lang('MailSubjectForwardShort').": ".$message_reply_info['title'].']';
+        $default['title'] = '['.get_lang('MailSubjectForwardShort').": ".Security::remove_XSS($message_reply_info['title']).']';
         $form->addHidden('forward_id', $forwardId);
         $form->addHidden('save_form', 'save_form');
         $receiverInfo = api_get_user_info($message_reply_info['user_receiver_id']);
 
         $forwardMessage = '---------- '.get_lang('ForwardedMessage').' ---------'.'<br />';
         $forwardMessage .= get_lang('Date').': '.api_get_local_time($message_reply_info['send_date']).'<br />';
-        $forwardMessage .= get_lang('Subject').': '.$message_reply_info['title'].'<br />';
+        $forwardMessage .= get_lang('Subject').': '.Security::remove_XSS($message_reply_info['title']).'<br />';
         $forwardMessage .= get_lang('To').': '.$receiverInfo['complete_name'].' - '.$receiverInfo['email'].' <br />';
         $default['content'] = '<p><br/></p>'.$forwardMessage.'<br />'.Security::filter_terms($message_reply_info['content']);
     }
@@ -417,4 +425,4 @@ $tpl->assign('content_inbox', $message_content);
 $social_layout = $tpl->get_template('message/inbox.html.twig');
 $content = $tpl->fetch($social_layout);
 $tpl->assign('content', $content);
-$tpl->display_one_col_template();
+$tpl->display_one_col_template();

+ 10 - 2
main/messages/outbox.php

@@ -13,9 +13,17 @@ if (api_get_setting('allow_message_tool') != 'true') {
     api_not_allowed(true);
 }
 
+$logInfo = [
+    'tool' => 'Messages',
+    'tool_id' => 0,
+    'tool_id_detail' => 0,
+    'action' => isset($_GET['action']) ? $_GET['action'] : 'outbox',
+    'action_details' => '',
+];
+Event::registerLog($logInfo);
+
 $allowSocial = api_get_setting('allow_social_tool') == 'true';
 $allowMessage = api_get_setting('allow_message_tool') == 'true';
-$show_message = null;
 
 if (isset($_GET['messages_page_nr'])) {
     if ($allowSocial && $allowMessage) {
@@ -112,4 +120,4 @@ $social_layout = $tpl->get_template('message/inbox.html.twig');
 $content = $tpl->fetch($social_layout);
 $tpl->assign('message', $show_message);
 $tpl->assign('content', $content);
-$tpl->display_one_col_template();
+$tpl->display_one_col_template();

+ 7 - 14
main/mySpace/index.php

@@ -12,16 +12,17 @@ $cidReset = true;
 
 require_once __DIR__.'/../inc/global.inc.php';
 
-$htmlHeadXtra[] = api_get_jqgrid_js();
+// Access control
+api_block_anonymous_users();
 
+$htmlHeadXtra[] = api_get_jqgrid_js();
+$htmlHeadXtra[] = '<script type="text/javascript" src="'.api_get_path(WEB_PUBLIC_PATH).'assets/jquery.easy-pie-chart/dist/jquery.easypiechart.js"></script>';
 // the section (for the tabs)
 $this_section = SECTION_TRACKING;
 //for HTML editor repository
-//Session::erase('this_section');
-
 ob_start();
 $nameTools = get_lang('MySpace');
-$export_csv = isset($_GET['export']) && $_GET['export'] == 'csv' ? true : false;
+$export_csv = isset($_GET['export']) && $_GET['export'] === 'csv' ? true : false;
 $display = isset($_GET['display']) ? Security::remove_XSS($_GET['display']) : null;
 $csv_content = [];
 $user_id = api_get_user_id();
@@ -30,18 +31,12 @@ $is_coach = api_is_coach($session_id);
 $is_platform_admin = api_is_platform_admin();
 $is_drh = api_is_drh();
 $is_session_admin = api_is_session_admin();
-$title = '';
 $skipData = api_get_configuration_value('tracking_skip_generic_data');
 
-// Access control
-api_block_anonymous_users();
-
 $logInfo = [
     'tool' => SECTION_TRACKING,
     'tool_id' => 0,
     'tool_id_detail' => 0,
-    'action' => '',
-    'action_details' => '',
 ];
 Event::registerLog($logInfo);
 
@@ -69,7 +64,6 @@ $calendarMenuAdded = false;
 
 if ($is_platform_admin) {
     if ($view == 'admin') {
-        $title = get_lang('CoachList');
         $menu_items[] = Display::url(
             Display::return_icon('teacher.png', get_lang('TeacherInterface'), [], ICON_SIZE_MEDIUM),
             api_get_self().'?view=teacher'
@@ -311,13 +305,12 @@ $form = new FormValidator(
     api_get_path(WEB_CODE_PATH).'mySpace/student.php'
 );
 $form = Tracking::setUserSearchForm($form);
-$skipData = api_get_configuration_value('tracking_skip_generic_data');
 
 $totalTimeSpent = null;
 $averageScore = null;
 $posts = null;
 
-if ($skipData == false) {
+if ($skipData === false) {
     if (!empty($students)) {
         // Students
         $studentIds = array_values($students);
@@ -400,7 +393,7 @@ $view->assign('form', $form->returnForm());
 $view->assign('actions', Display::toolbarAction('toolbar', [$actionsLeft, $actionsRight]));
 $view->assign('title', get_lang('Students').' ('.$numberStudents.')');
 
-$template = $view->get_template('my_space/index.html.twig');
+$template = $view->get_template('my_space/index.tpl');
 $content = $view->fetch($template);
 $view->assign('content', $content);
 $view->display_one_col_template();

+ 10 - 5
main/mySpace/myStudents.php

@@ -650,7 +650,7 @@ while ($row = Database::fetch_array($rs, 'ASSOC')) {
 
 $isDrhOfCourse = CourseManager::isUserSubscribedInCourseAsDrh(
     api_get_user_id(),
-    api_get_course_info()
+    $courseInfo
 );
 
 if (api_is_drh() && !api_is_platform_admin()) {
@@ -666,7 +666,7 @@ if (api_is_drh() && !api_is_platform_admin()) {
                 api_not_allowed(true);
             }*/
         } else {
-            if (!($isDrhOfCourse)) {
+            if (!$isDrhOfCourse) {
                 if (api_is_drh() &&
                    !UserManager::is_user_followed_by_drh($student_id, api_get_user_id())
                 ) {
@@ -763,16 +763,21 @@ if ($isAllow) {
 echo '</div>';
 
 // is the user online ?
+$online = get_lang('No');
 if (user_is_online($student_id)) {
     $online = get_lang('Yes');
-} else {
-    $online = get_lang('No');
 }
 
 // get average of score and average of progress by student
 $avg_student_progress = $avg_student_score = 0;
 
-if (CourseManager::is_user_subscribed_in_course($user_info['user_id'], $course_code, true)) {
+if (empty($sessionId)) {
+    $isSubscribedToCourse = CourseManager::is_user_subscribed_in_course($user_info['user_id'], $course_code);
+} else {
+    $isSubscribedToCourse = CourseManager::is_user_subscribed_in_course($user_info['user_id'], $course_code, true, $sessionId);
+}
+
+if ($isSubscribedToCourse) {
     $avg_student_progress = Tracking::get_avg_student_progress(
         $user_info['user_id'],
         $course_code,

+ 7 - 5
main/search/search_suggestions.php

@@ -115,9 +115,11 @@ function get_suggestions_from_search_engine($q)
     echo json_encode($json);
 }
 
-$q = strtolower($_GET["term"]);
-if (!$q) {
-    return;
+if (isset($_GET['term'])) {
+    $q = strtolower($_GET['term']);
+    if (!$q) {
+        return;
+    }
+    //echo $q . "| value\n";
+    get_suggestions_from_search_engine($q);
 }
-//echo $q . "| value\n";
-get_suggestions_from_search_engine($q);

+ 1 - 1
main/session/about.php

@@ -239,7 +239,7 @@ $template->assign(
 $template->assign('has_requirements', $hasRequirements);
 $template->assign('sequences', $sessionRequirements);
 $template->assign('is_premium', $sessionIsPremium);
-$layout = $template->get_template('session/about.html.twig');
+$layout = $template->get_template('session/about.tpl');
 $content = $template->fetch($layout);
 //$template->assign('header', $session->getName());
 $template->assign('content', $content);

+ 4 - 1
main/session/add_edit_users_to_session.php

@@ -15,7 +15,10 @@ $xajax->registerFunction('search_users');
 // setting the section (for the tabs)
 $this_section = SECTION_PLATFORM_ADMIN;
 
-$id_session = intval($_GET['id_session']);
+$id_session = isset($_GET['id_session']) ? (int) $_GET['id_session'] : 0;
+if (empty($id_session)) {
+    api_not_allowed(true);
+}
 $addProcess = isset($_GET['add']) ? Security::remove_XSS($_GET['add']) : null;
 
 SessionManager::protectSession($id_session);

+ 1 - 1
main/session/add_teachers_to_session.php

@@ -24,7 +24,7 @@ $tool_name = get_lang('EnrollTrainersFromExistingSessions');
 
 $form_sent = 0;
 $errorMsg = '';
-$id = intval($_GET['id']);
+$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
 
 SessionManager::protectSession($id);
 

+ 1 - 1
main/session/add_users_to_session.php

@@ -15,7 +15,7 @@ $xajax->registerFunction('search_users');
 // setting the section (for the tabs)
 $this_section = SECTION_PLATFORM_ADMIN;
 
-$id_session = intval($_GET['id_session']);
+$id_session = isset($_GET['id_session']) ? (int) $_GET['id_session'] : 0;
 $addProcess = isset($_GET['add']) ? Security::remove_XSS($_GET['add']) : null;
 
 SessionManager::protectSession($id_session);

+ 1 - 1
main/session/add_users_to_session_course.php

@@ -15,7 +15,7 @@ $xajax->registerFunction('search_users');
 // setting the section (for the tabs)
 $this_section = SECTION_PLATFORM_ADMIN;
 
-$id_session = (int) $_GET['id_session'];
+$id_session = isset($_GET['id_session']) ? (int) $_GET['id_session'] : 0;
 $courseId = isset($_GET['course_id']) ? (int) $_GET['course_id'] : 0;
 
 if (empty($id_session) || empty($courseId)) {

+ 40 - 31
main/session/index.php

@@ -13,11 +13,12 @@ use ChamiloSession as Session;
 $cidReset = true;
 require_once __DIR__.'/../inc/global.inc.php';
 
-if (empty($_GET['session_id'])) {
+$session_id = isset($_GET['session_id']) ? (int) $_GET['session_id'] : 0;
+
+if (empty($session_id)) {
     api_not_allowed(true);
 }
 
-$session_id = isset($_GET['session_id']) ? intval($_GET['session_id']) : null;
 $sessionField = new ExtraFieldValue('session');
 $valueAllowVisitors = $sessionField->get_values_by_handler_and_field_variable(
     $session_id,
@@ -32,7 +33,7 @@ if (!$allowVisitors) {
 
 $this_section = SECTION_COURSES;
 $htmlHeadXtra[] = api_get_jqgrid_js();
-$course_id = isset($_GET['course_id']) ? intval($_GET['course_id']) : null;
+$course_id = isset($_GET['course_id']) ? (int) $_GET['course_id'] : null;
 Session::write('id_session', $session_id);
 
 // Clear the exercise session just in case
@@ -43,12 +44,13 @@ Session::erase('duration_time');
 $userId = api_get_user_id();
 $session_info = SessionManager::fetch($session_id);
 $session_list = SessionManager::get_sessions_by_coach(api_get_user_id());
-$course_list = SessionManager::get_course_list_by_session_id($session_id);
-
+$courseList = SessionManager::get_course_list_by_session_id($session_id);
 $userIsGeneralCoach = SessionManager::user_is_general_coach($userId, $session_id);
 
 $user_course_list = [];
-foreach ($course_list as $course) {
+$exerciseListPerCourse = [];
+
+foreach ($courseList as $course) {
     $status = SessionManager::get_user_status_in_course_session(
         $userId,
         $course['real_id'],
@@ -57,6 +59,32 @@ foreach ($course_list as $course) {
     if ($status !== false || api_is_platform_admin() || $userIsGeneralCoach) {
         $user_course_list[] = $course['real_id'];
     }
+
+    $exerciseList = ExerciseLib::get_all_exercises_for_course_id(
+        $course,
+        $session_id,
+        $course['real_id'],
+        true
+    );
+
+    $exerciseListNew = [];
+    if (!empty($exerciseList)) {
+        // Exercises
+        foreach ($exerciseList as $exerciseInfo) {
+            $exerciseId = $exerciseInfo['id'];
+            $visibility = api_get_item_visibility(
+                $course,
+                TOOL_QUIZ,
+                $exerciseId,
+                $session_id
+            );
+            if ($visibility == 0) {
+                continue;
+            }
+            $exerciseListNew[] = $exerciseInfo;
+        }
+    }
+    $exerciseListPerCourse[$course['real_id']] = $exerciseListNew;
 }
 
 if (empty($user_course_list)) {
@@ -66,9 +94,8 @@ if (empty($user_course_list)) {
 $my_session_list = [];
 $final_array = [];
 $new_course_list = [];
-
-if (!empty($course_list)) {
-    foreach ($course_list as $course_data) {
+if (!empty($courseList)) {
+    foreach ($courseList as $course_data) {
         if (api_is_platform_admin()) {
             $course_data['title'] = Display::url(
                 $course_data['title'],
@@ -107,17 +134,7 @@ if (!empty($course_list)) {
         }
 
         $course_info = api_get_course_info($course_data['code']);
-        $exercise_count = count(
-            ExerciseLib::get_all_exercises(
-                $course_info,
-                $session_id,
-                true,
-                null,
-                false,
-                1
-            )
-        );
-
+        $exerciseCount = count($exerciseListPerCourse[$course_info['real_id']]);
         $max_mutation_date = '';
         $last_date = Tracking::get_last_connection_date_on_the_course(
             api_get_user_id(),
@@ -158,7 +175,7 @@ if (!empty($course_list)) {
             'title' => $course_data['title'].$icons,
             //  'recent_lps' => $icons,
             //'max_mutation_date' => substr(api_get_local_time($max_mutation_date),0,10),
-            'exercise_count' => $exercise_count,
+            'exercise_count' => $exerciseCount,
             'lp_count' => $lp_count,
         ];
     }
@@ -223,7 +240,6 @@ if (empty($session_id)) {
 //Final data to be show
 $my_real_array = $new_exercises = [];
 $now = time();
-$courseList = SessionManager::get_course_list_by_session_id($session_id);
 
 if (!empty($courseList)) {
     foreach ($courseList as $courseInfo) {
@@ -237,18 +253,11 @@ if (!empty($courseList)) {
             $session_id
         );
 
-        $exerciseList = ExerciseLib::get_all_exercises_for_course_id(
-            $courseInfo,
-            $session_id,
-            $courseId,
-            true
-        );
+        $exerciseList = $exerciseListPerCourse[$courseId];
 
         if (!empty($exerciseList)) {
             // Exercises
             foreach ($exerciseList as $exerciseInfo) {
-                $exerciseId = $exerciseInfo['id'];
-
                 if ($exerciseInfo['start_time'] == '0000-00-00 00:00:00') {
                     $start_date = '-';
                 } else {
@@ -264,7 +273,7 @@ if (!empty($courseList)) {
                 $best_score = '';
                 if (!empty($best_score_data)) {
                     $best_score = ExerciseLib::show_score(
-                        $best_score_data['exe_result'],
+                        $best_score_data['score'],
                         $best_score_data['max_score']
                     );
                 }

+ 15 - 9
main/session/resume_session.php

@@ -99,12 +99,17 @@ switch ($action) {
         }
 
         if (!empty($_GET['class'])) {
+            $class = (int) $_GET['class'];
             $result = Database::query(
                 "DELETE FROM $tbl_session_rel_class
-                 WHERE session_id='$sessionId' AND class_id=".intval($_GET['class'])
+                 WHERE session_id = $sessionId
+                  AND class_id = $class"
             );
             $nbr_affected_rows = Database::affected_rows($result);
-            Database::query("UPDATE $tbl_session SET nbr_classes=nbr_classes-$nbr_affected_rows WHERE id='$sessionId'");
+            Database::query(
+                "UPDATE $tbl_session 
+                SET nbr_classes = nbr_classes - $nbr_affected_rows 
+                WHERE id = $sessionId");
         }
 
         if (!empty($_GET['user'])) {
@@ -145,7 +150,7 @@ $url = Display::url(
 );
 $courseListToShow = Display::page_subheader(get_lang('CourseList').$url);
 
-$courseListToShow .= '<table id="session-list-course" class="table table-bordered">
+$courseListToShow .= '<table id="session-list-course" class="table table-hover data_table">
 <tr>
   <th width="35%">'.get_lang('CourseTitle').'</th>
   <th width="30%">'.get_lang('CourseCoach').'</th>
@@ -209,11 +214,12 @@ if ($session->getNbrCourses() === 0) {
         // hide_course_breadcrumb the parameter has been added to hide the name
         // of the course, that appeared in the default $interbreadcrumb
         $courseItem .= '<tr>
-			<td class="title">'.
-            Display::url(
+			<td class="title">'
+            .Display::url(
                 $course->getTitle().' ('.$course->getVisualCode().')',
                 $courseUrl
-            ).'</td>';
+            )
+            .'</td>';
         $courseItem .= '<td>'.($namesOfCoaches ? implode('<br>', $namesOfCoaches) : get_lang('None')).'</td>';
         $courseItem .= '<td>'.$numberOfUsers.'</td>';
         $courseItem .= '<td>';
@@ -284,8 +290,8 @@ $url .= Display::url(
     $codePath."session/session_user_import.php?id_session=$sessionId"
 );
 $url .= Display::url(
-    Display::return_icon('export_csv.png', get_lang('ExportUsers'), [], ICON_SIZE_SMALL),
-    api_get_path(WEB_CODE_PATH)."user/user_export.php?file_type=csv&session=$sessionId&addcsvheader=1"
+    Display::return_icon('export_csv.png', get_lang('ExportUsers')),
+    $codePath."user/user_export.php?file_type=csv&session=$sessionId&addcsvheader=1"
 );
 
 $userListToShow = Display::page_subheader(get_lang('UserList').$url);
@@ -415,5 +421,5 @@ $tpl->assign('user_list', $userListToShow);
 $tpl->assign('dependencies', $dependencies);
 $tpl->assign('requirements', $requirements);
 
-$layout = $tpl->get_template('session/resume_session.html.twig');
+$layout = $tpl->get_template('session/resume_session.tpl');
 $tpl->display($layout);

+ 92 - 38
main/session/session_category_list.php

@@ -26,15 +26,17 @@ function selectAll(idCheck,numRows,action) {
 $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
 $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
 
-$page = isset($_GET['page']) ? intval($_GET['page']) : null;
+$page = isset($_GET['page']) ? (int) $_GET['page'] : null;
 $action = isset($_REQUEST['action']) ? Security::remove_XSS($_REQUEST['action']) : null;
-$sort = isset($_GET['sort']) && in_array($_GET['sort'], ['name', 'nbr_session', 'date_start', 'date_end']) ? Security::remove_XSS($_GET['sort']) : 'name';
+$sort = isset($_GET['sort']) && in_array($_GET['sort'], ['name', 'nbr_session', 'date_start', 'date_end'])
+    ? Security::remove_XSS($_GET['sort'])
+    : 'name';
 $idChecked = isset($_REQUEST['idChecked']) ? Security::remove_XSS($_REQUEST['idChecked']) : null;
 $order = isset($_REQUEST['order']) ? Security::remove_XSS($_REQUEST['order']) : 'ASC';
-$keyword = isset($_GET['keyword']) ? Security::remove_XSS($_GET['keyword']) : null;
+$keyword = isset($_REQUEST['keyword']) ? Security::remove_XSS($_REQUEST['keyword']) : null;
 
-if ($action == 'delete_on_session' || $action == 'delete_off_session') {
-    $delete_session = ($action == 'delete_on_session') ? true : false;
+if ($action === 'delete_on_session' || $action === 'delete_off_session') {
+    $delete_session = $action == 'delete_on_session' ? true : false;
     SessionManager::delete_session_category($idChecked, $delete_session);
     Display::addFlash(Display::return_message(get_lang('SessionCategoryDelete')));
     header('Location: '.api_get_self().'?sort='.$sort);
@@ -43,8 +45,8 @@ if ($action == 'delete_on_session' || $action == 'delete_off_session') {
 
 $interbreadcrumb[] = ['url' => 'session_list.php', 'name' => get_lang('SessionList')];
 
-if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
-    $interbreadcrumb[] = ["url" => 'session_category_list.php', "name" => get_lang('ListSessionCategory')];
+if (isset($_GET['search']) && $_GET['search'] === 'advanced') {
+    $interbreadcrumb[] = ['url' => 'session_category_list.php', 'name' => get_lang('ListSessionCategory')];
     $tool_name = get_lang('SearchASession');
     Display :: display_header($tool_name);
     $form = new FormValidator('advanced_search', 'get');
@@ -64,9 +66,9 @@ if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
     //if user is crfp admin only list its sessions
     $where = null;
     if (!api_is_platform_admin()) {
-        $where .= (empty($keyword) ? "" : " WHERE name LIKE '%".Database::escape_string(trim($_REQUEST['keyword']))."%'");
+        $where .= empty($keyword) ? "" : " WHERE name LIKE '%".Database::escape_string(trim($keyword))."%'";
     } else {
-        $where .= (empty($keyword) ? "" : " WHERE name LIKE '%".Database::escape_string(trim($_REQUEST['keyword']))."%'");
+        $where .= empty($keyword) ? "" : " WHERE name LIKE '%".Database::escape_string(trim($keyword))."%'";
     }
     if (empty($where)) {
         $where = " WHERE access_url_id = ".api_get_current_access_url_id()." ";
@@ -103,10 +105,14 @@ if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
         <div class="row">
             <div class="col-md-6">
             <?php
-                echo '<a href="'.api_get_path(WEB_CODE_PATH).'session/session_category_add.php">'.
-                        Display::return_icon('new_folder.png', get_lang('AddSessionCategory'), '', ICON_SIZE_MEDIUM).'</a>
-                      <a href="'.api_get_path(WEB_CODE_PATH).'session/session_list.php">'.
-                        Display::return_icon('session.png', get_lang('ListSession'), '', ICON_SIZE_MEDIUM).'</a>'; ?>
+                echo Display::url(
+                    Display::return_icon('new_folder.png', get_lang('AddSessionCategory'), [], ICON_SIZE_MEDIUM),
+                    api_get_path(WEB_CODE_PATH).'session/session_category_add.php'
+                );
+    echo Display::url(
+                    Display::return_icon('session.png', get_lang('ListSession'), [], ICON_SIZE_MEDIUM),
+                    api_get_path(WEB_CODE_PATH).'session/session_list.php'
+                ); ?>
             </div>
             <div class="col-md-6">
                 <div class="float-right">
@@ -114,25 +120,34 @@ if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
                     <div class="form-group">
                     <input class="form-control" type="text" name="keyword" value="<?php echo $keyword; ?>"
                     aria-label="<?php echo get_lang('Search'); ?>"/>
-                    <button class="btn btn-default" type="submit" name="name" value="<?php echo get_lang('Search'); ?>"><em class="fa fa-search"></em> <?php echo get_lang('Search'); ?></button>
+                            <button class="btn btn-default" type="submit" name="name"
+                                    value="<?php echo get_lang('Search'); ?>"><em
+                                        class="fa fa-search"></em> <?php echo get_lang('Search'); ?></button>
                     <!-- <a href="session_list.php?search=advanced"><?php echo get_lang('AdvancedSearch'); ?></a> -->
                     </div>
                 </form>
                 </div>
             </div>
         </div>
-    <form method="post" action="<?php echo api_get_self(); ?>?action=delete&sort=<?php echo $sort; ?>" onsubmit="javascript:if(!confirm('<?php echo get_lang('ConfirmYourChoice'); ?>')) return false;">
     </div>
-    <div align="left">
+    <form method="post" action="<?php echo api_get_self(); ?>?action=delete&sort=<?php echo $sort; ?>"
+          onsubmit="if(!confirm('<?php echo get_lang('ConfirmYourChoice'); ?>')) return false;">
         <?php
         if (count($Sessions) == 0 && isset($_POST['keyword'])) {
-            echo get_lang('NoSearchResults');
-            echo '</div>';
+            echo Display::return_message(get_lang('NoSearchResults'), 'warning');
         } else {
             if ($num > $limit) {
+                ?>
+                <div>
+                    <?php
                 if ($page) {
                     ?>
-                    <a href="<?php echo api_get_self(); ?>?page=<?php echo $page - 1; ?>&sort=<?php echo $sort; ?>&order=<?php echo Security::remove_XSS($order); ?>&keyword=<?php echo $keyword; ?><?php echo @$cond_url; ?>"><?php echo get_lang('Previous'); ?></a>
+                        <a href="<?php echo api_get_self(); ?>?page=<?php echo $page
+                            - 1; ?>&sort=<?php echo $sort; ?>&order=<?php echo Security::remove_XSS(
+                            $order
+                        ); ?>&keyword=<?php echo $keyword; ?><?php echo @$cond_url; ?>"><?php echo get_lang(
+                                'Previous'
+                            ); ?></a>
                     <?php
                 } else {
                     echo get_lang('Previous');
@@ -141,21 +156,31 @@ if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
                 <?php
                 if ($nbr_results > $limit) {
                     ?>
-                    <a href="<?php echo api_get_self(); ?>?page=<?php echo $page + 1; ?>&sort=<?php echo $sort; ?>&order=<?php echo Security::remove_XSS($order); ?>&keyword=<?php echo $keyword; ?><?php echo @$cond_url; ?>"><?php echo get_lang('Next'); ?></a>
+                        <a href="<?php echo api_get_self(); ?>?page=<?php echo $page
+                            + 1; ?>&sort=<?php echo $sort; ?>&order=<?php echo Security::remove_XSS(
+                            $order
+                        ); ?>&keyword=<?php echo $keyword; ?><?php echo @$cond_url; ?>"><?php echo get_lang(
+                                'Next'
+                            ); ?></a>
                     <?php
                 } else {
                     echo get_lang('Next');
-                }
             } ?>
         </div>
+                <?php
+            } ?>
 
-        <table class="table" width="100%">
+            <table class="data_table" width="100%">
             <tr>
                 <th>&nbsp;</th>
-                <th><a href="<?php echo api_get_self(); ?>?sort=name&order=<?php echo ($sort == 'name') ? $order : 'ASC'; ?>"><?php echo get_lang('SessionCategoryName'); ?></a></th>
-                <th><a href="<?php echo api_get_self(); ?>?sort=nbr_session&order=<?php echo ($sort == 'nbr_session') ? $order : 'ASC'; ?>"><?php echo get_lang('NumberOfSession'); ?></a></th>
-                <th><a href="<?php echo api_get_self(); ?>?sort=date_start&order=<?php echo ($sort == 'date_start') ? $order : 'ASC'; ?>"><?php echo get_lang('StartDate'); ?></a></th>
-                <th><a href="<?php echo api_get_self(); ?>?sort=date_end&order=<?php echo ($sort == 'date_end') ? $order : 'ASC'; ?>"><?php echo get_lang('EndDate'); ?></a></th>
+                    <th><a href="<?php echo api_get_self(); ?>?sort=name&order=<?php echo ($sort == 'name') ? $order
+                            : 'ASC'; ?>"><?php echo get_lang('SessionCategoryName'); ?></a></th>
+                    <th><a href="<?php echo api_get_self(); ?>?sort=nbr_session&order=<?php echo ($sort
+                            == 'nbr_session') ? $order : 'ASC'; ?>"><?php echo get_lang('NumberOfSession'); ?></a></th>
+                    <th><a href="<?php echo api_get_self(); ?>?sort=date_start&order=<?php echo ($sort == 'date_start')
+                            ? $order : 'ASC'; ?>"><?php echo get_lang('StartDate'); ?></a></th>
+                    <th><a href="<?php echo api_get_self(); ?>?sort=date_end&order=<?php echo ($sort == 'date_end')
+                            ? $order : 'ASC'; ?>"><?php echo get_lang('EndDate'); ?></a></th>
                 <th><?php echo get_lang('Actions'); ?></th>
             </tr>
 
@@ -177,9 +202,11 @@ if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
                 $rs = Database::query($sql);
                 list($nb_courses) = Database::fetch_array($rs); ?>
                 <tr class="<?php echo $i ? 'row_odd' : 'row_even'; ?>">
-                    <td><input type="checkbox" id="idChecked_<?php echo $x; ?>" name="idChecked[]" value="<?php echo $enreg['id']; ?>"></td>
+                        <td><input type="checkbox" id="idChecked_<?php echo $x; ?>" name="idChecked[]"
+                                   value="<?php echo $enreg['id']; ?>"></td>
                     <td><?php echo api_htmlentities($enreg['name'], ENT_QUOTES, $charset); ?></td>
-                    <td><?php echo "<a href=\"session_list.php?id_category=".$enreg['id']."\">".$nb_courses." Session(s) </a>"; ?></td>
+                        <td><?php echo "<a href=\"session_list.php?id_category=".$enreg['id']."\">".$nb_courses
+                                ." Session(s) </a>"; ?></td>
                     <td><?php echo api_format_date($enreg['date_start'], DATE_FORMAT_SHORT); ?></td>
                     <td>
                         <?php
@@ -193,7 +220,11 @@ if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
                         <a href="session_category_edit.php?&id=<?php echo $enreg['id']; ?>">
                             <?php Display::display_icon('edit.png', get_lang('Edit'), [], ICON_SIZE_SMALL); ?>
                         </a>
-                        <a href="<?php echo api_get_self(); ?>?sort=<?php echo $sort; ?>&action=delete_off_session&idChecked=<?php echo $enreg['id']; ?>" onclick="javascript:if(!confirm('<?php echo get_lang('ConfirmYourChoice'); ?>')) return false;">
+                            <a href="<?php echo api_get_self(
+                            ); ?>?sort=<?php echo $sort; ?>&action=delete_off_session&idChecked=<?php echo $enreg['id']; ?>"
+                               onclick="if(!confirm('<?php echo get_lang(
+                                   'ConfirmYourChoice'
+                               ); ?>')) return false;">
                             <?php Display::display_icon('delete.png', get_lang('Delete'), [], ICON_SIZE_SMALL); ?>
                         </a>
                     </td>
@@ -205,12 +236,15 @@ if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
             unset($Sessions); ?>
         </table>
         <br />
-        <div align="left">
+            <div>
         <?php
         if ($num > $limit) {
             if ($page) {
                 ?>
-                    <a href="<?php echo api_get_self(); ?>?page=<?php echo $page - 1; ?>&sort=<?php echo $sort; ?>&order=<?php echo Security::remove_XSS($_REQUEST['order']); ?>&keyword=<?php echo $_REQUEST['keyword']; ?><?php echo @$cond_url; ?>">
+                        <a href="<?php echo api_get_self(); ?>?page=<?php echo $page
+                            - 1; ?>&sort=<?php echo $sort; ?>&order=<?php echo Security::remove_XSS(
+                            $_REQUEST['order']
+                        ); ?>&keyword=<?php echo $_REQUEST['keyword']; ?><?php echo @$cond_url; ?>">
                         <?php echo get_lang('Previous'); ?></a>
                 <?php
             } else {
@@ -221,7 +255,10 @@ if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
             if ($nbr_results > $limit) {
                 ?>
 
-                <a href="<?php echo api_get_self(); ?>?page=<?php echo $page + 1; ?>&sort=<?php echo $sort; ?>&order=<?php echo Security::remove_XSS($_REQUEST['order']); ?>&keyword=<?php echo $_REQUEST['keyword']; ?><?php echo @$cond_url; ?>">
+                        <a href="<?php echo api_get_self(); ?>?page=<?php echo $page
+                            + 1; ?>&sort=<?php echo $sort; ?>&order=<?php echo Security::remove_XSS(
+                            $_REQUEST['order']
+                        ); ?>&keyword=<?php echo $_REQUEST['keyword']; ?><?php echo @$cond_url; ?>">
                     <?php echo get_lang('Next'); ?></a>
 
                 <?php
@@ -230,20 +267,37 @@ if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
             }
         } ?>
         </div>
+            <div class="row">
+                <div class="col-sm-4">
         <div class="btn-group">
-            <a class="btn btn-outline-secondary" href="#" onclick="selectAll('idChecked',<?php echo $x; ?>,'true');return false;"><?php echo get_lang('SelectAll'); ?></a>
-            <a class="btn btn-outline-secondary" href="#" onclick="selectAll('idChecked',<?php echo $x; ?>,'false');return false;"><?php echo get_lang('UnSelectAll'); ?></a>
+                        <button type="button" class="btn btn-default" onclick="selectAll('idChecked',<?php echo $x; ?>,'true');">
+                            <?php echo get_lang('SelectAll'); ?>
+                        </button>
+                        <button type="button" class="btn btn-default" onclick="selectAll('idChecked',<?php echo $x; ?>,'false');">
+                            <?php echo get_lang('UnSelectAll'); ?>
+                        </button>
         </div>
-        <div class="list-category">
+                </div>
+                <div class="col-sm-6">
             <select class="selectpicker show-tick form-control" name="action">
-                <option value="delete_off_session" selected="selected"><?php echo get_lang('DeleteSelectedSessionCategory'); ?></option>
-                <option value="delete_on_session"><?php echo get_lang('DeleteSelectedFullSessionCategory'); ?></option>
+                        <option value="delete_off_session" selected="selected">
+                            <?php echo get_lang('DeleteSelectedSessionCategory'); ?>
+                        </option>
+                        <option value="delete_on_session">
+                            <?php echo get_lang('DeleteSelectedFullSessionCategory'); ?>
+                        </option>
             </select>
         </div>
-        <button class="btn btn-success" type="submit" name="name" value="<?php echo get_lang('Ok'); ?>"><?php echo get_lang('Ok'); ?></button>
+                <div class="col-sm-2">
+                    <button class="btn btn-success" type="submit" name="name" value="<?php echo get_lang('Ok'); ?>">
+                        <?php echo get_lang('Ok'); ?>
+                    </button>
+                </div>
+            </div>
     <?php
         } ?>
     </table>
+    </form>
 <?php
 }
 Display::display_footer();

+ 1 - 1
main/session/session_course_edit.php

@@ -10,7 +10,7 @@ $cidReset = true;
 
 require_once __DIR__.'/../inc/global.inc.php';
 
-$id_session = intval($_GET['id_session']);
+$id_session = isset($_GET['id_session']) ? (int) $_GET['id_session'] : 0;
 SessionManager::protectSession($id_session);
 $course_code = $_GET['course_code'];
 $course_info = api_get_course_info($_REQUEST['course_code']);

+ 1 - 1
main/session/session_course_list.php

@@ -10,7 +10,7 @@ require_once __DIR__.'/../inc/global.inc.php';
 // setting the section (for the tabs)
 $this_section = SECTION_PLATFORM_ADMIN;
 
-$id_session = intval($_GET['id_session']);
+$id_session = isset($_GET['id_session']) ? (int) $_GET['id_session'] : 0;
 SessionManager::protectSession($id_session);
 
 // Database Table Definitions

+ 2 - 3
main/session/session_course_user.php

@@ -14,12 +14,11 @@ require_once __DIR__.'/../inc/global.inc.php';
 // setting the section (for the tabs)
 $this_section = SECTION_PLATFORM_ADMIN;
 $tool_name = get_lang('EditSessionCoursesByUser');
+$id_session = isset($_GET['id_session']) ? (int) $_GET['id_session'] : 0;
+SessionManager::protectSession($id_session);
 
-$id_session = intval($_GET['id_session']);
 $id_user = intval($_GET['id_user']);
 
-SessionManager::protectSession($id_session);
-
 $em = Database::getManager();
 /** @var Session $session */
 $session = $em->find('ChamiloCoreBundle:Session', $id_session);

+ 3 - 3
main/session/session_course_user_list.php

@@ -15,7 +15,7 @@ $tbl_session_rel_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
 $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
 $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
 
-$id_session = intval($_GET['id_session']);
+$id_session = isset($_GET['id_session']) ? (int) $_GET['id_session'] : 0;
 SessionManager::protectSession($id_session);
 
 if (empty($id_session)) {
@@ -110,10 +110,10 @@ $nbr_results = sizeof($users);
 
 $tool_name = get_lang('Session').': '.$session_name.' - '.get_lang('Course').': '.$course_title;
 
-$interbreadcrumb[] = ["url" => "session_list.php", "name" => get_lang('SessionList')];
+$interbreadcrumb[] = ['url' => 'session_list.php', 'name' => get_lang('SessionList')];
 $interbreadcrumb[] = [
     'url' => "resume_session.php?id_session=".$id_session,
-    "name" => get_lang('SessionOverview'),
+    'name' => get_lang('SessionOverview'),
 ];
 
 Display::display_header($tool_name);

+ 1 - 0
main/session/session_export.php

@@ -14,6 +14,7 @@ $this_section = SECTION_PLATFORM_ADMIN;
 api_protect_admin_script(true);
 
 $session_id = isset($_GET['session_id']) ? intval($_GET['session_id']) : 0;
+
 $formSent = 0;
 $errorMsg = '';
 

+ 16 - 3
main/session/session_list.php

@@ -6,17 +6,16 @@
  *
  * @package chamilo.admin
  */
-
 $cidReset = true;
 
 require_once __DIR__.'/../inc/global.inc.php';
 $this_section = SECTION_PLATFORM_ADMIN;
 
+SessionManager::protectSession(null, false);
+
 // Add the JS needed to use the jqgrid
 $htmlHeadXtra[] = api_get_jqgrid_js();
 
-SessionManager::protectSession(null, false);
-
 $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : null;
 $idChecked = isset($_REQUEST['idChecked']) ? $_REQUEST['idChecked'] : null;
 $list_type = isset($_REQUEST['list_type']) ? $_REQUEST['list_type'] : 'simple';
@@ -263,6 +262,8 @@ $orderUrl = api_get_path(WEB_AJAX_PATH).'session.ajax.php?a=order';
                     multipleSearch : true,
                     overlay : false,
                     width: 'auto',
+                    caption: '<?php echo addslashes(get_lang('Search')); ?>',
+                    formclass:'data_table',
                     onSearch : function() {
                         var postdata = grid.jqGrid('getGridParam', 'postData');
 
@@ -334,6 +335,16 @@ $orderUrl = api_get_path(WEB_AJAX_PATH).'session.ajax.php?a=order';
             }
             ?>
 
+            //Select first elements by default
+            var searchDialogAll = $("#fbox_"+grid[0].id);
+            searchDialogAll.addClass("table");
+            var searchDialog = $("#searchmodfbox_"+grid[0].id);
+            searchDialog.addClass("ui-jqgrid ui-widget ui-widget-content ui-corner-all");
+            searchDialog.css({position:"adsolute", "z-index":"100", "float":"left", "top":"55%", "left" : "25%", "padding" : "5px", "border": "1px solid #CCC"})
+            var gbox = $("#gbox_"+grid[0].id);
+            gbox.before(searchDialog);
+            gbox.css({clear:"left"});
+
             //Select first elements by default
             $('.input-elm').each(function(){
                 $(this).find('option:first').attr('selected', 'selected');
@@ -392,6 +403,8 @@ if (api_is_platform_admin()) {
     echo '</div>';
 }
 echo '</div>';
+echo '<div id="session-table" class="table-responsive">';
 echo Display::grid_html('sessions');
+echo '</div>';
 
 Display::display_footer();

+ 1 - 3
main/social/friends.php

@@ -70,7 +70,7 @@ $interbreadcrumb[] = ['url' => 'profile.php', 'name' => get_lang('SocialNetwork'
 $interbreadcrumb[] = ['url' => '#', 'name' => get_lang('Friends')];
 
 //Block Social Menu
-$social_menu_block = SocialManager::getMenuSocial('friends');
+$social_menu_block = SocialManager::show_social_menu('friends');
 $user_id = api_get_user_id();
 $name_search = isset($_POST['search_name_q']) ? $_POST['search_name_q'] : null;
 $number_friends = 0;
@@ -111,7 +111,6 @@ if (count($friends) == 0) {
 
     $friend_html = '<div id="whoisonline">';
     $friend_html .= '<div class="row">';
-    $friend_html .= '<div class="col-lg-12">';
     $number_friends = count($friends);
     $j = 0;
 
@@ -131,7 +130,6 @@ if (count($friends) == 0) {
     }
     $friend_html .= '</div>';
     $friend_html .= '</div>';
-    $friend_html .= '</div>';
     $social_right_content .= $friend_html;
 }
 $social_right_content .= '</div>';

+ 2 - 2
main/social/group_add.php

@@ -42,8 +42,8 @@ $interbreadcrumb[] = ['url' => 'home.php', 'name' => get_lang('Social')];
 $interbreadcrumb[] = ['url' => 'groups.php', 'name' => get_lang('Groups')];
 $interbreadcrumb[] = ['url' => '#', 'name' => $nameTools];
 
-$social_avatar_block = SocialManager::getAvatarBlock('group_add');
-$social_menu_block = SocialManager::getMenuSocial('group_add');
+$social_avatar_block = SocialManager::show_social_avatar_block('group_add');
+$social_menu_block = SocialManager::show_social_menu('group_add');
 $social_right_content = $form->returnForm();
 
 $tpl = new Template(null);

+ 1 - 1
main/social/group_edit.php

@@ -54,7 +54,7 @@ if ($form->validate()) {
     exit();
 }
 
-$social_left_content = SocialManager::getMenuSocial('group_edit', $group_id);
+$social_left_content = SocialManager::show_social_menu('group_edit', $group_id);
 $social_right_content = $form->returnForm();
 
 $tpl = new Template(get_lang('Edit'));

+ 3 - 3
main/social/group_invitation.php

@@ -143,7 +143,7 @@ if (is_array($Users) && count($Users) > 0) {
     }
 }
 
-$social_left_content = SocialManager::getMenuSocial('invite_friends', $group_id);
+$social_left_content = SocialManager::show_social_menu('invite_friends', $group_id);
 $social_right_content = '<h3 class="group-title">'.Security::remove_XSS($group_info['name'], STUDENT, true).'</h3>';
 
 if (count($nosessionUsersList) == 0) {
@@ -159,7 +159,7 @@ if (count($nosessionUsersList) == 0) {
     $social_right_content .= '<br />';
 }
 
-$form = new FormValidator('group_invitation', 'post', api_get_self().'?id='.$group_id);
+$form = new FormValidator('frm_invitation', 'post', api_get_self().'?id='.$group_id);
 $form->addHidden('form_sent', 1);
 $form->addHidden('id', $group_id);
 
@@ -201,7 +201,7 @@ if (is_array($members) && count($members) > 0) {
 
 $tpl = new Template(null);
 SocialManager::setSocialUserBlock($tpl, api_get_user_id(), 'groups', $group_id);
-$social_menu_block = SocialManager::getMenuSocial('member_list', $group_id);
+$social_menu_block = SocialManager::show_social_menu('member_list', $group_id);
 $tpl->assign('social_menu_block', $social_menu_block);
 $tpl->setHelp('Groups');
 $tpl->assign('social_right_content', $social_right_content);

+ 2 - 2
main/social/group_members.php

@@ -112,11 +112,11 @@ $users = $userGroup->get_users_by_group(
 );
 $new_member_list = [];
 
-$social_avatar_block = SocialManager::getAvatarBlock(
+$social_avatar_block = SocialManager::show_social_avatar_block(
     'member_list',
     $group_id
 );
-$social_menu_block = SocialManager::getMenuSocial('member_list', $group_id);
+$social_menu_block = SocialManager::show_social_menu('member_list', $group_id);
 $social_right_content = '<h2>'.$group_info['name'].'</h2>';
 
 foreach ($users as $user) {

+ 2 - 2
main/social/group_topics.php

@@ -185,7 +185,7 @@ $interbreadcrumb[] = ['url' => 'groups.php', 'name' => get_lang('Groups')];
 $interbreadcrumb[] = ['url' => 'group_view.php?id='.$group_id, 'name' => Security::remove_XSS($group_info['name'])];
 $interbreadcrumb[] = ['url' => '#', 'name' => get_lang('Discussions')];
 
-$social_left_content = SocialManager::getMenuSocial('member_list', $group_id);
+$social_left_content = SocialManager::show_social_menu('member_list', $group_id);
 $show_message = null;
 if (!empty($show_message)) {
     $social_right_content .= Display::return_message($show_message, 'confirmation');
@@ -197,7 +197,7 @@ $group_message = MessageManager::display_message_for_group(
     $message_id
 );
 
-$social_menu_block = SocialManager::getMenuSocial('member_list', $group_id);
+$social_menu_block = SocialManager::show_social_menu('member_list', $group_id);
 
 $tpl = new Template(null);
 $tpl->setHelp('Groups');

+ 1 - 0
main/social/group_view.php

@@ -324,6 +324,7 @@ $tpl = new Template(null);
 // Block Social Avatar
 SocialManager::setSocialUserBlock($tpl, api_get_user_id(), 'groups', $group_id);
 
+$social_menu_block = SocialManager::show_social_menu('groups', $group_id);
 $tpl->setHelp('Groups');
 $tpl->assign('create_link', $create_thread_link);
 $tpl->assign('is_group_member', $is_group_member);

+ 2 - 2
main/social/group_waiting_list.php

@@ -86,7 +86,7 @@ $users = $usergroup->get_users_by_group(
 );
 
 $new_member_list = [];
-$social_left_content = SocialManager::getMenuSocial('waiting_list', $group_id);
+$social_left_content = SocialManager::show_social_menu('waiting_list', $group_id);
 
 // Display form
 foreach ($users as $user) {
@@ -112,7 +112,7 @@ if (empty($new_member_list) > 0) {
 $tpl = new Template(null);
 
 SocialManager::setSocialUserBlock($tpl, api_get_user_id(), 'groups', $group_id);
-$social_menu_block = SocialManager::getMenuSocial('member_list', $group_id);
+$social_menu_block = SocialManager::show_social_menu('member_list', $group_id);
 $tpl->assign('social_menu_block', $social_menu_block);
 $tpl->setHelp('Groups');
 $tpl->assign('members', $new_member_list);

+ 4 - 0
main/social/groups.php

@@ -374,7 +374,11 @@ if (isset($_GET['view']) && $_GET['view'] == 'mygroups') {
     $show_menu = $_GET['view'];
 }
 
+$social_menu_block = SocialManager::show_social_menu($show_menu);
+$templateName = 'social/groups.tpl';
+
 $tpl->setHelp('Groups');
+$tpl->assign('social_menu_block', $social_menu_block);
 $tpl->assign('social_right_content', $social_right_content);
 $social_layout = $tpl->get_template('social/groups.html.twig');
 $content = $tpl->fetch($social_layout);

+ 1 - 49
main/social/home.php

@@ -29,54 +29,6 @@ if (api_get_setting('allow_social_tool') !== 'true') {
 
 $userGroup = new UserGroup();
 
-//fast upload image
-/*if (api_get_setting('profile', 'picture') == 'true') {
-    $form = new FormValidator('profile', 'post', 'home.php', null, []);
-
-    //	PICTURE
-    $form->addElement('file', 'picture', get_lang('AddImage'));
-    $form->addProgress();
-    if (!empty($user_data['picture_uri'])) {
-        $form->addElement(
-            'checkbox',
-            'remove_picture',
-            null,
-            get_lang('DelImage')
-        );
-    }
-    $allowed_picture_types = api_get_supported_image_extensions();
-    $form->addRule(
-        'picture',
-        get_lang('OnlyImagesAllowed').' ('.implode(
-            ',',
-            $allowed_picture_types
-        ).')',
-        'filetype',
-        $allowed_picture_types
-    );
-    $form->addButtonSave(get_lang('SaveSettings'), 'apply_change');
-
-    if ($form->validate()) {
-        $user_data = $form->getSubmitValues();
-        // upload picture if a new one is provided
-        if ($_FILES['picture']['size']) {
-            if ($new_picture = UserManager::update_user_picture(
-                api_get_user_id(),
-                $_FILES['picture']['name'],
-                $_FILES['picture']['tmp_name']
-            )) {
-                $table_user = Database::get_main_table(TABLE_MAIN_USER);
-                $sql = "UPDATE $table_user
-                        SET
-                            picture_uri = '$new_picture'
-                        WHERE user_id =  ".api_get_user_id();
-
-                $result = Database::query($sql);
-            }
-        }
-    }
-}*/
-
 SocialManager::handlePosts(api_get_self());
 
 $threadList = SocialManager::getThreadList($user_id);
@@ -95,7 +47,7 @@ SocialManager::getScrollJs($countPost, $htmlHeadXtra);
 $social_menu_block = SocialManager::show_social_menu('home');
 
 $social_search_block = Display::panel(
-    UserManager::getSearchForm(''),
+    UserManager::get_search_form(''),
     get_lang('SearchUsers')
 );
 

+ 7 - 8
main/social/invitations.php

@@ -84,34 +84,33 @@ if (is_array($_GET) && count($_GET) > 0) {
 $content = null;
 
 // Block Menu Social
-$social_menu_block = SocialManager::getMenuSocial('invitations');
+$social_menu_block = SocialManager::show_social_menu('invitations');
 // Block Invitations
 $socialInvitationsBlock = '<div id="id_response" align="center"></div>';
 
 $user_id = api_get_user_id();
-$list_get_invitation = SocialManager::get_list_invitation_of_friends_by_user_id($user_id, 0);
+$list_get_invitation = SocialManager::get_list_invitation_of_friends_by_user_id($user_id);
 $list_get_invitation_sent = SocialManager::get_list_invitation_sent_by_user_id($user_id);
 $pending_invitations = $userGroupModel->get_groups_by_user(
     $user_id,
     GROUP_USER_PERMISSION_PENDING_INVITATION
 );
-$number_loop = count($list_get_invitation);
+$numberLoop = count($list_get_invitation);
 
-$total_invitations = $number_loop + count($list_get_invitation_sent) + count($pending_invitations);
+$total_invitations = $numberLoop + count($list_get_invitation_sent) + count($pending_invitations);
 
-if ($total_invitations == 0 && count($_GET) <= 0) {
+if (count($_GET) <= 0) {
     $socialInvitationsBlock .= '<div class="row">
         <div class="col-md-12">
             <a class="btn btn-success" href="search.php"><em class="fa fa-search"></em> '.
                 get_lang('TryAndFindSomeFriends').'
             </a>
             </div>
-        </div>';
+        </div><br />';
 }
 
-if ($number_loop != 0) {
+if ($numberLoop != 0) {
     $invitationHtml = '';
-
     foreach ($list_get_invitation as $invitation) {
         $sender_user_id = $invitation['user_sender_id'];
         $user_info = api_get_user_info($sender_user_id);

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff