Browse Source

Minor - merge with 1.11.x

jmontoyaa 6 years ago
parent
commit
ef667cb9c1
100 changed files with 2825 additions and 2371 deletions
  1. 12 8
      main/admin/user_information.php
  2. 33 18
      main/announcements/announcements.php
  3. 1 1
      main/attendance/attendance_add.php
  4. 1 1
      main/attendance/attendance_edit.php
  5. 2 1
      main/auth/courses.php
  6. 41 22
      main/auth/courses_controller.php
  7. 18 17
      main/auth/inscription.php
  8. 1 1
      main/course_description/add.php
  9. 1 1
      main/course_description/edit.php
  10. 10 9
      main/course_description/index.php
  11. 0 4
      main/course_home/2column.php
  12. 5 1
      main/course_home/3column.php
  13. 0 2
      main/course_home/activity.php
  14. 79 27
      main/course_home/course_home.php
  15. 0 2
      main/course_home/vertical_activity.php
  16. 2 1
      main/course_info/delete_course.php
  17. 110 9
      main/course_info/infocours.php
  18. 39 11
      main/course_info/maintenance.php
  19. 1 1
      main/course_info/maintenance_coach.php
  20. 7 2
      main/course_info/tools.php
  21. 0 1
      main/course_progress/index.php
  22. 2 2
      main/course_progress/layout.php
  23. 1 2
      main/course_progress/thematic.php
  24. 1 1
      main/course_progress/thematic_advance.php
  25. 2 2
      main/course_progress/thematic_controller.php
  26. 2 2
      main/course_progress/thematic_plan.php
  27. 8 3
      main/coursecopy/copy_course_session.php
  28. 1 1
      main/cron/fix_course_spent_time.php
  29. 3 2
      main/document/create_audio.php
  30. 4 3
      main/document/create_document.php
  31. 1 0
      main/document/create_draw.php
  32. 57 20
      main/document/document.php
  33. 32 22
      main/document/edit_document.php
  34. 1 0
      main/document/record_audio.php
  35. 8 64
      main/document/showinframes.php
  36. 1 1
      main/document/upload.php
  37. 60 179
      main/document/webcam_clip.php
  38. 3 3
      main/exercise/TestCategory.php
  39. 1 0
      main/exercise/answer.class.php
  40. 86 92
      main/exercise/exercise.class.php
  41. 43 4
      main/exercise/exercise.php
  42. 39 7
      main/exercise/exercise_reminder.php
  43. 26 3
      main/exercise/exercise_report.php
  44. 18 2
      main/exercise/exercise_result.class.php
  45. 1 1
      main/exercise/exercise_result.php
  46. 5 5
      main/exercise/exercise_show.php
  47. 5 5
      main/exercise/fill_blanks.class.php
  48. 1 1
      main/exercise/global_multiple_answer.class.php
  49. 28 10
      main/exercise/hotpotatoes.php
  50. 45 22
      main/exercise/hotspot_answers.as.php
  51. 26 13
      main/exercise/question.class.php
  52. 6 0
      main/exercise/question_create.php
  53. 16 17
      main/exercise/upload_exercise.php
  54. 1 0
      main/forum/forumfunction.inc.php
  55. 8 2
      main/gradebook/certificate_report.php
  56. 31 45
      main/gradebook/index.php
  57. 1 1
      main/gradebook/lib/GradebookUtils.php
  58. 1 7
      main/gradebook/lib/be/abstractlink.class.php
  59. 2 42
      main/gradebook/lib/be/attendancelink.class.php
  60. 5 3
      main/gradebook/lib/be/dropboxlink.class.php
  61. 55 68
      main/gradebook/lib/be/exerciselink.class.php
  62. 1 3
      main/gradebook/lib/be/forumthreadlink.class.php
  63. 1 32
      main/gradebook/lib/be/learnpathlink.class.php
  64. 1 31
      main/gradebook/lib/be/studentpublicationlink.class.php
  65. 1 37
      main/gradebook/lib/be/surveylink.class.php
  66. 0 1
      main/gradebook/lib/fe/flatviewtable.class.php
  67. 1 1
      main/gradebook/lib/fe/gradebooktable.class.php
  68. 9 2
      main/gradebook/lib/flatview_data_generator.class.php
  69. 162 64
      main/group/group_category.php
  70. 108 13
      main/group/settings.php
  71. 1 1
      main/inc/ajax/admin.ajax.php
  72. 3 0
      main/inc/ajax/document.ajax.php
  73. 25 6
      main/inc/ajax/exercise.ajax.php
  74. 2 3
      main/inc/ajax/lp.ajax.php
  75. 2 4
      main/inc/ajax/model.ajax.php
  76. 8 0
      main/inc/ajax/myspace.ajax.php
  77. 9 3
      main/inc/ajax/session.ajax.php
  78. 94 2
      main/inc/ajax/statistics.ajax.php
  79. 14 6
      main/inc/introductionSection.inc.php
  80. 2 4
      main/inc/lib/AnnouncementEmail.php
  81. 11 11
      main/inc/lib/AnnouncementManager.php
  82. 549 0
      main/inc/lib/CoursesAndSessionsCatalog.class.php
  83. 13 7
      main/inc/lib/api.lib.php
  84. 6 395
      main/inc/lib/auth.lib.php
  85. 3 3
      main/inc/lib/banner.lib.php
  86. 18 17
      main/inc/lib/blog.lib.php
  87. 4 23
      main/inc/lib/career.lib.php
  88. 171 191
      main/inc/lib/course.lib.php
  89. 16 249
      main/inc/lib/course_category.lib.php
  90. 1 2
      main/inc/lib/display.lib.php
  91. 56 126
      main/inc/lib/document.lib.php
  92. 253 228
      main/inc/lib/events.lib.php
  93. 11 12
      main/inc/lib/exercise.lib.php
  94. 4 3
      main/inc/lib/fileUpload.lib.php
  95. 3 0
      main/inc/lib/formvalidator/Element/DateRangePicker.php
  96. 1 5
      main/inc/lib/formvalidator/Element/DateTimePicker.php
  97. 23 23
      main/inc/lib/formvalidator/FormValidator.class.php
  98. 230 55
      main/inc/lib/groupmanager.lib.php
  99. 1 2
      main/inc/lib/javascript/ckeditor/config_js.php
  100. 7 9
      main/inc/lib/javascript/hotspot/js/hotspot.js

+ 12 - 8
main/admin/user_information.php

@@ -620,7 +620,13 @@ if ($studentBossList) {
     echo $studentBossListToString;
 }
 
-$hrmList = $userEntity->getHrm();
+$em = Database::getManager();
+$userRepository = $em->getRepository('ChamiloUserBundle:User');
+
+$hrmList = $userRepository->getAssignedHrmUserList(
+    $userEntity->getId(),
+    api_get_current_access_url_id()
+);
 
 if ($hrmList) {
     echo Display::page_subheader(get_lang('HrmList'));
@@ -629,21 +635,20 @@ if ($hrmList) {
     /** @var UserRelUser $hrm */
     foreach ($hrmList as $hrm) {
         $hrmInfo = api_get_user_info($hrm->getFriendUserId());
-        $userPicture = isset($hrmInfo["avatar_medium"]) ? $hrmInfo["avatar_medium"] : $hrmInfo["avatar"];
 
+        $userPicture = isset($hrmInfo['avatar_medium']) ? $hrmInfo['avatar_medium'] : $hrmInfo['avatar'];
         echo '<div class="col-sm-4 col-md-3">';
         echo '<div class="media">';
         echo '<div class="media-left">';
         echo Display::img($userPicture, $hrmInfo['complete_name'], ['class' => 'media-object'], false);
         echo '</div>';
         echo '<div class="media-body">';
-        echo '<h4 class="media-heading">'.$hrmInfo['complete_name'].'</h4>';
+        echo '<h4 class="media-heading">'.$hrmInfo['complete_name_with_message_link'].'</h4>';
         echo $hrmInfo['username'];
         echo '</div>';
         echo '</div>';
         echo '</div>';
     }
-
     echo '</div>';
 }
 
@@ -656,7 +661,7 @@ if ($user['status'] == DRH) {
 
         foreach ($usersAssigned as $userAssigned) {
             $userAssigned = api_get_user_info($userAssigned['user_id']);
-            $userPicture = isset($userAssigned["avatar_medium"]) ? $userAssigned["avatar_medium"] : $userAssigned["avatar"];
+            $userPicture = isset($userAssigned['avatar_medium']) ? $userAssigned['avatar_medium'] : $userAssigned['avatar'];
 
             echo '<div class="col-sm-4 col-md-3">';
             echo '<div class="media">';
@@ -664,13 +669,12 @@ if ($user['status'] == DRH) {
             echo Display::img($userPicture, $userAssigned['complete_name'], ['class' => 'media-object'], false);
             echo '</div>';
             echo '<div class="media-body">';
-            echo '<h4 class="media-heading">'.$userAssigned['complete_name'].'</h4>';
-            echo $userAssigned['username'];
+            echo '<h4 class="media-heading">'.$userAssigned['complete_name_with_message_link'].'</h4>';
+            echo $userAssigned['official_code'];
             echo '</div>';
             echo '</div>';
             echo '</div>';
         }
-
         echo '</div>';
     }
 }

+ 33 - 18
main/announcements/announcements.php

@@ -97,6 +97,11 @@ $searchFormToString = '';
 
 switch ($action) {
     case 'move':
+
+        if (!$allowToEdit) {
+            api_not_allowed(true);
+        }
+
         /* Move announcement up/down */
         if (!empty($_GET['down'])) {
             $thisAnnouncementId = intval($_GET['down']);
@@ -282,7 +287,7 @@ switch ($action) {
 
         if (empty($count)) {
             $html = '';
-            if ($allowToEdit && (empty($_GET['origin']) or $_GET['origin'] !== 'learnpath')) {
+            if ($allowToEdit && (empty($_GET['origin']) || $_GET['origin'] !== 'learnpath')) {
                 $html .= '<div id="no-data-view">';
                 $html .= '<h3>'.get_lang('Announcements').'</h3>';
                 $html .= Display::return_icon('valves.png', '', [], 64);
@@ -346,6 +351,10 @@ switch ($action) {
                     api_not_allowed();
                 }
 
+                if (!$allowToEdit) {
+                    api_not_allowed(true);
+                }
+
                 if (!api_is_session_general_coach() ||
                     api_is_element_in_the_session(TOOL_ANNOUNCEMENT, $_GET['id'])
                 ) {
@@ -368,6 +377,10 @@ switch ($action) {
             api_not_allowed(true);
         }
 
+        if (!$allowToEdit) {
+            api_not_allowed(true);
+        }
+
         // DISPLAY ADD ANNOUNCEMENT COMMAND
         $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
         $url = api_get_self().'?action='.$action.'&id='.$id.'&'.api_get_cidreq();
@@ -616,25 +629,27 @@ switch ($action) {
                             $sendToUsersInSession
                         );
                     }
-
-                    Display::addFlash(
-                        Display::return_message(
-                            get_lang('AnnouncementAdded'),
-                            'success'
-                        )
-                    );
-
-                    // Send mail
-                    if (isset($data['email_ann']) && $data['email_ann']) {
-                        AnnouncementManager::sendEmail(
-                            api_get_course_info(),
-                            api_get_session_id(),
-                            $insert_id,
-                            $sendToUsersInSession
+                    if ($insert_id) {
+                        Display::addFlash(
+                            Display::return_message(
+                                get_lang('AnnouncementAdded'),
+                                'success'
+                            )
                         );
+
+                        // Send mail
+                        if (isset($data['email_ann']) && $data['email_ann']) {
+                            AnnouncementManager::sendEmail(
+                                api_get_course_info(),
+                                api_get_session_id(),
+                                $insert_id,
+                                $sendToUsersInSession
+                            );
+                        }
+                        header('Location: '.$homeUrl);
+                        exit;
                     }
-                    header('Location: '.$homeUrl);
-                    exit;
+                    api_not_allowed(true);
                 } // end condition token
             }
         }

+ 1 - 1
main/attendance/attendance_add.php

@@ -36,7 +36,7 @@ $form->addHtmlEditor(
     get_lang('Description'),
     false,
     false,
-    ['ToolbarSet' => 'TrainingDescription', 'Width' => '100%', 'Height' => '150']
+    ['ToolbarSet' => 'Basic', 'Width' => '100%', 'Height' => '150']
 );
 
 // Advanced Parameters

+ 1 - 1
main/attendance/attendance_edit.php

@@ -40,7 +40,7 @@ $form->addHtmlEditor(
     false,
     false,
     [
-        'ToolbarSet' => 'TrainingDescription',
+        'ToolbarSet' => 'Basic',
         'Width' => '100%',
         'Height' => '200',
     ]

+ 2 - 1
main/auth/courses.php

@@ -19,7 +19,7 @@ require_once __DIR__.'/../inc/global.inc.php';
 $ctok = Security::get_existing_token();
 
 // Get Limit data
-$limit = CourseCategory::getLimitArray();
+$limit = CoursesController::getLimitArray();
 
 // Section for the tabs.
 $this_section = SECTION_CATALOG;
@@ -187,6 +187,7 @@ if (isset($_POST['unsubscribe'])) {
         $courseController->unsubscribe_user_from_course($_POST['unsubscribe']);
     }
 }
+
 switch ($action) {
     case 'subscribe_user_with_password':
         $courseController->subscribe_user(

+ 41 - 22
main/auth/courses_controller.php

@@ -1,7 +1,10 @@
 <?php
 /* For licensing terms, see /license.txt */
 
+use Chamilo\CoreBundle\Entity\Repository\SequenceRepository;
 use Chamilo\CoreBundle\Entity\SequenceResource;
+use Chamilo\CoreBundle\Entity\SessionRelCourse;
+use Chamilo\CoreBundle\Entity\Tag;
 
 /**
  * Class CoursesController.
@@ -41,7 +44,7 @@ class CoursesController
     {
         $data = [];
         $data['user_courses'] = $this->model->get_courses_of_user(api_get_user_id());
-        $data['user_course_categories'] = $this->model->get_user_course_categories();
+        $data['user_course_categories'] = CourseManager::get_user_course_categories(api_get_user_id());
         $data['courses_in_category'] = $this->model->get_courses_in_category();
         $data['action'] = $action;
         $data['message'] = $message;
@@ -66,7 +69,7 @@ class CoursesController
         api_block_anonymous_users();
 
         $data = [];
-        $data['user_course_categories'] = $this->model->get_user_course_categories();
+        $data['user_course_categories'] = CourseManager::get_user_course_categories(api_get_user_id());
 
         $stok = Security::get_token();
         $actions = Display::url(
@@ -114,11 +117,11 @@ class CoursesController
         $limit = []
     ) {
         $data = [];
-        $browse_course_categories = $this->model->browse_course_categories();
-        $data['countCoursesInCategory'] = $this->model->count_courses_in_category($category_code);
+        $browse_course_categories = CoursesAndSessionsCatalog::getCourseCategories();
+        $data['countCoursesInCategory'] = CourseCategory::countCoursesInCategory($category_code);
         if ($action === 'display_random_courses') {
             // Random value is used instead limit filter
-            $data['browse_courses_in_category'] = $this->model->browse_courses_in_category(
+            $data['browse_courses_in_category'] = CoursesAndSessionsCatalog::getCoursesInCategory(
                 null,
                 12
             );
@@ -127,8 +130,8 @@ class CoursesController
             if (!isset($category_code)) {
                 $category_code = $browse_course_categories[0][1]['code']; // by default first category
             }
-            $limit = isset($limit) ? $limit : CourseCategory::getLimitArray();
-            $data['browse_courses_in_category'] = $this->model->browse_courses_in_category(
+            $limit = isset($limit) ? $limit : self::getLimitArray();
+            $data['browse_courses_in_category'] = CoursesAndSessionsCatalog::getCoursesInCategory(
                 $category_code,
                 null,
                 $limit
@@ -192,13 +195,13 @@ class CoursesController
         $justVisible = false
     ) {
         $data = [];
-        $limit = !empty($limit) ? $limit : CourseCategory::getLimitArray();
-        $browse_course_categories = $this->model->browse_course_categories();
-        $data['countCoursesInCategory'] = $this->model->count_courses_in_category(
+        $limit = !empty($limit) ? $limit : self::getLimitArray();
+        $browse_course_categories = CoursesAndSessionsCatalog::getCourseCategories();
+        $data['countCoursesInCategory'] = CourseCategory::countCoursesInCategory(
             'ALL',
             $search_term
         );
-        $data['browse_courses_in_category'] = $this->model->search_courses(
+        $data['browse_courses_in_category'] = CoursesAndSessionsCatalog::search_courses(
             $search_term,
             $limit,
             $justVisible
@@ -430,7 +433,7 @@ class CoursesController
         }
 
         if (!empty($search_term)) {
-            $this->search_courses($search_term, $message, $error);
+            CoursesAndSessionsCatalog::search_courses($search_term, $message, $error);
         } else {
             $this->courses_categories(
                 'subcribe',
@@ -455,7 +458,7 @@ class CoursesController
         $hiddenLinks = false,
         $limit = null
     ) {
-        $categories = $this->model->browse_course_categories();
+        $categories = CoursesAndSessionsCatalog::getCourseCategories();
         $html = '';
         if (!empty($categories)) {
             $action = 'display_courses';
@@ -578,7 +581,7 @@ class CoursesController
         $btnBing = false
     ) {
         if ($btnBing) {
-            $btnBing = 'btn-lg';
+            $btnBing = 'btn-lg btn-block';
         } else {
             $btnBing = 'btn-sm';
         }
@@ -595,7 +598,7 @@ class CoursesController
                 get_lang('CheckRequirements'),
                 $url,
                 'shield',
-                'default',
+                'info',
                 [
                     'class' => $btnBing.' ajax',
                     'data-title' => get_lang('CheckRequirements'),
@@ -711,9 +714,9 @@ class CoursesController
     {
         $date = isset($_POST['date']) ? $_POST['date'] : date('Y-m-d');
         $hiddenLinks = isset($_GET['hidden_links']) ? intval($_GET['hidden_links']) == 1 : false;
-        $limit = isset($limit) ? $limit : CourseCategory::getLimitArray();
-        $countSessions = $this->model->countSessions($date);
-        $sessions = $this->model->browseSessions($date, $limit);
+        $limit = isset($limit) ? $limit : self::getLimitArray();
+        $countSessions = SessionManager::countSessionsByEndDate($date);
+        $sessions = CoursesAndSessionsCatalog::browseSessions($date, $limit);
 
         $pageTotal = intval(intval($countSessions) / $limit['length']);
         // Do NOT show pagination if only one page or less
@@ -765,7 +768,7 @@ class CoursesController
             'subscribe'
         );
 
-        $sessions = $this->model->browseSessionsByTags($searchTag, $limit);
+        $sessions = CoursesAndSessionsCatalog::browseSessionsByTags($searchTag, $limit);
         $sessionsBlocks = $this->getFormattedSessionsBlock($sessions);
 
         $tpl = new Template();
@@ -803,7 +806,7 @@ class CoursesController
         );
         $searchDate = isset($_POST['date']) ? $_POST['date'] : date('Y-m-d');
 
-        $sessions = $this->model->browseSessionsBySearch($q, $limit);
+        $sessions = CoursesAndSessionsCatalog::browseSessionsBySearch($q, $limit);
         $sessionsBlocks = $this->getFormattedSessionsBlock($sessions);
 
         $tpl = new Template();
@@ -823,6 +826,21 @@ class CoursesController
         $tpl->display($contentTemplate);
     }
 
+    /**
+     * @return array
+     */
+    public static function getLimitArray()
+    {
+        $pageCurrent = isset($_REQUEST['pageCurrent']) ? intval($_GET['pageCurrent']) : 1;
+        $pageLength = isset($_REQUEST['pageLength']) ? intval($_GET['pageLength']) : CoursesAndSessionsCatalog::PAGE_LENGTH;
+
+        return [
+            'start' => ($pageCurrent - 1) * $pageLength,
+            'current' => $pageCurrent,
+            'length' => $pageLength,
+        ];
+    }
+
     /**
      * Get the formatted data for sessions block to be displayed on Session Catalog page.
      *
@@ -865,13 +883,13 @@ class CoursesController
                 $sessionRelCourses = $sessionRelCourseRepo->findBy([
                     'session' => $session,
                 ]);
-
+                /** @var SessionRelCourse $sessionRelCourse */
                 foreach ($sessionRelCourses as $sessionRelCourse) {
                     $courseTags = $extraFieldRelTagRepo->getTags(
                         $tagsField,
                         $sessionRelCourse->getCourse()->getId()
                     );
-
+                    /** @var Tag $tag */
                     foreach ($courseTags as $tag) {
                         $sessionCourseTags[] = $tag->getTag();
                     }
@@ -882,6 +900,7 @@ class CoursesController
                 $sessionCourseTags = array_unique($sessionCourseTags);
             }
 
+            /** @var SequenceRepository $repo */
             $repo = $entityManager->getRepository('ChamiloCoreBundle:SequenceResource');
             $sequences = $repo->getRequirementsAndDependenciesWithinSequences(
                 $session->getId(),

+ 18 - 17
main/auth/inscription.php

@@ -30,20 +30,17 @@ if ($allowedFieldsConfiguration !== false) {
     $allowedFields = isset($allowedFieldsConfiguration['fields']) ? $allowedFieldsConfiguration['fields'] : [];
     $allowedFields['extra_fields'] = isset($allowedFieldsConfiguration['extra_fields']) ? $allowedFieldsConfiguration['extra_fields'] : [];
 }
-$gMapsPlugin = GoogleMapsPlugin::create();
-$geolocalization = $gMapsPlugin->get('enable_api') === 'true';
 
-if ($geolocalization) {
-    $gmapsApiKey = $gMapsPlugin->get('api_key');
-    $htmlHeadXtra[] = '<script type="text/javascript" src="//maps.googleapis.com/maps/api/js?sensor=true&key='.$gmapsApiKey.'" ></script>';
+$gMapsPlugin = GoogleMapsPlugin::create();
+if ($gMapsPlugin->get('enable_api') === 'true') {
+    $key = $gMapsPlugin->get('api_key');
+    $htmlHeadXtra[] = '<script type="text/javascript" src="//maps.googleapis.com/maps/api/js?sensor=true&key='.$key.'" ></script>';
 }
 
 $htmlHeadXtra[] = api_get_password_checker_js('#username', '#pass1');
 // User is not allowed if Terms and Conditions are disabled and
 // registration is disabled too.
-$isNotAllowedHere = api_get_setting('allow_terms_conditions') === 'false' && api_get_setting(
-        'allow_registration'
-    ) === 'false';
+$isNotAllowedHere = api_get_setting('allow_terms_conditions') === 'false' && api_get_setting('allow_registration') === 'false';
 
 if ($isNotAllowedHere) {
     api_not_allowed(true, get_lang('RegistrationDisabled'));
@@ -58,13 +55,9 @@ if (!empty($_SESSION['user_language_choice'])) {
 }
 
 $form = new FormValidator('registration');
-
 $user_already_registered_show_terms = false;
 if (api_get_setting('allow_terms_conditions') == 'true') {
     $user_already_registered_show_terms = isset($_SESSION['term_and_condition']['user_id']);
-    /*if (api_is_anonymous() === true) {
-        $user_already_registered_show_terms = false;
-    }*/
 }
 
 $sessionPremiumChecker = Session::read('SessionIsPremium');
@@ -90,7 +83,7 @@ if (!empty($course_code_redirect)) {
     Session::write('exercise_redirect', $exercise_redirect);
 }
 
-if ($user_already_registered_show_terms === false) {
+if ($user_already_registered_show_terms === false && api_get_setting('allow_registration') === 'true') {
     // STUDENT/TEACHER
     if (api_get_setting('allow_registration_as_teacher') != 'false') {
         if (in_array('status', $allowedFields)) {
@@ -481,9 +474,8 @@ if (!empty($_GET['openid_msg']) && $_GET['openid_msg'] == 'idnotfound') {
 }
 
 $showTerms = false;
-
 // Terms and conditions
-if (api_get_setting('allow_terms_conditions') == 'true') {
+if (api_get_setting('allow_terms_conditions') == 'true' && $user_already_registered_show_terms) {
     if (!api_is_platform_admin()) {
         if (api_get_setting('show_terms_if_profile_completed') === 'true') {
             $userInfo = api_get_user_info();
@@ -535,6 +527,7 @@ if (api_get_setting('allow_terms_conditions') == 'true') {
 
 $allowDoubleValidation = api_get_configuration_value('allow_double_validation_in_registration');
 
+$formContainsSendButton = false;
 if ($allowDoubleValidation && $showTerms == false) {
     $htmlHeadXtra[] = '<script>
         $(document).ready(function() {
@@ -560,8 +553,17 @@ if ($allowDoubleValidation && $showTerms == false) {
     );
     $form->addButton('submit', get_lang('RegisterUser'), '', 'primary');
     $form->addHtml('</div>');
+    $formContainsSendButton = true;
 } else {
-    $form->addButtonNext(get_lang('RegisterUser'));
+    if (api_get_setting('allow_registration') === 'true' || $user_already_registered_show_terms || $showTerms) {
+        $form->addButtonNext(get_lang('RegisterUser'));
+        $formContainsSendButton = true;
+    }
+}
+
+// Blocks page because there's any action to do.
+if (!$formContainsSendButton) {
+    api_not_allowed(true);
 }
 
 $course_code_redirect = Session::read('course_redirect');
@@ -668,7 +670,6 @@ if ($form->validate()) {
         if ($user_id) {
             // Storing the extended profile
             $store_extended = false;
-
             $sql = "UPDATE ".Database::get_main_table(TABLE_MAIN_USER)." SET ";
 
             if (api_get_setting('extended_profile') == 'true' &&

+ 1 - 1
main/course_description/add.php

@@ -80,7 +80,7 @@ $form->addHtmlEditor(
     true,
     false,
     [
-        'ToolbarSet' => 'TrainingDescription',
+        'ToolbarSet' => 'Basic',
         'Width' => '100%',
         'Height' => '200',
     ]

+ 1 - 1
main/course_description/edit.php

@@ -98,7 +98,7 @@ $form->addHtmlEditor(
     true,
     false,
     [
-        'ToolbarSet' => 'TrainingDescription',
+        'ToolbarSet' => 'Basic',
         'Width' => '100%',
         'Height' => '200',
     ]

+ 10 - 9
main/course_description/index.php

@@ -75,6 +75,13 @@ if ($description_type >= 9) {
 // course description controller object
 $course_description_controller = new CourseDescriptionController();
 
+// block access
+if (in_array($action, ['add', 'edit', 'delete']) &&
+    !api_is_allowed_to_edit(null, true)
+) {
+    api_not_allowed(true);
+}
+
 // Actions to controller
 switch ($action) {
     case 'listing':
@@ -84,19 +91,13 @@ switch ($action) {
         $course_description_controller->listing(true);
         break;
     case 'add':
-        if (api_is_allowed_to_edit(null, true)) {
-            $course_description_controller->add();
-        }
+        $course_description_controller->add();
         break;
     case 'edit':
-        if (api_is_allowed_to_edit(null, true)) {
-            $course_description_controller->edit($id, $description_type);
-        }
+        $course_description_controller->edit($id, $description_type);
         break;
     case 'delete':
-        if (api_is_allowed_to_edit(null, true)) {
-            $course_description_controller->destroy($id);
-        }
+        $course_description_controller->destroy($id);
         break;
     default:
         $course_description_controller->listing();

+ 0 - 4
main/course_home/2column.php

@@ -86,11 +86,8 @@ if (api_is_allowed_to_edit(null, true) && !api_is_coach()) {
     $content .= get_lang('CourseAdminOnly');
     $content .= "</span>";
     $content .= "<table width=\"100%\">";
-
     $content .= CourseHome::show_tool_2column(TOOL_COURSE_ADMIN);
-
     /*	INACTIVE TOOLS - HIDDEN (GREY) LINKS */
-
     $content .= "<tr><td colspan=\"4\"><hr style='color:\"#4171B5\"' noshade=\"noshade\" size=\"1\" /></td></tr>\n".
             "<tr>\n".
             "<td colspan=\"4\">\n".
@@ -104,7 +101,6 @@ if (api_is_allowed_to_edit(null, true) && !api_is_coach()) {
 }
 
 /*	Tools for platform admin only */
-
 if (api_is_platform_admin() && api_is_allowed_to_edit(null, true) && !api_is_coach()) {
     $content .= '<div class="platformadminview">
     <span class="viewcaption">'.get_lang('PlatformAdminOnly').'</span>

+ 5 - 1
main/course_home/3column.php

@@ -58,7 +58,11 @@ if (api_is_allowed_to_edit(null, true)) {
         $sql = "SELECT * FROM $TBL_ACCUEIL WHERE c_id = $course_id AND id=$id";
         $result = Database::query($sql);
         $tool = Database::fetch_array($result);
-        $tool_name = @htmlspecialchars($tool['name'] != '' ? $tool['name'] : $tool['link'], ENT_QUOTES, api_get_system_encoding());
+        $tool_name = @htmlspecialchars(
+            $tool['name'] != '' ? $tool['name'] : $tool['link'],
+            ENT_QUOTES,
+            api_get_system_encoding()
+        );
         if ($tool['img'] != 'external.gif') {
             $tool['link'] = api_get_path(WEB_CODE_PATH).$tool['link'];
         }

+ 0 - 2
main/course_home/activity.php

@@ -135,7 +135,6 @@ if ($session_id === 0 && api_is_course_admin() && api_is_allowed_to_edit(null, t
     }
 } else {
     $tools = CourseHome::get_tools_category(TOOL_STUDENT_VIEW);
-
     $isDrhOfCourse = CourseManager::isUserSubscribedInCourseAsDrh(
         api_get_user_id(),
         api_get_course_info()
@@ -174,7 +173,6 @@ if ($session_id === 0 && api_is_course_admin() && api_is_allowed_to_edit(null, t
 
     if ($isDrhOfCourse) {
         $drhTool = CourseHome::get_tools_category(TOOL_DRH);
-
         $blocks[] = ['content' => CourseHome::show_tools_category($drhTool)];
     }
 }

+ 79 - 27
main/course_home/course_home.php

@@ -39,17 +39,15 @@ $(document).ready(function(){
         $(".btn-show-thematic").show(); //show using class
         $("#pross").fadeToggle(); //Not working collapse for Chrome
     });
+    
     $("#thematic-hide").click(function(){
         $(".btn-show-thematic").hide(); //show using class
         $(".btn-hide-thematic").show();
         $("#pross").fadeToggle(); //Not working collapse for Chrome
     });
-});
-
-$(document).ready(function() {
+    
 	$(".make_visible_and_invisible").attr("href", "javascript:void(0);");
 	$(".make_visible_and_invisible > img").click(function () {
-
 		make_visible = "visible.gif";
 		make_invisible = "invisible.gif";
 		path_name = $(this).attr("src");
@@ -129,7 +127,6 @@ if (api_is_invitee()) {
 // Deleting group session
 Session::erase('toolgroup');
 Session::erase('_gid');
-//$check = \Chamilo\CoreBundle\Framework\Container::$container->get('security.authorization_checker');
 
 $isSpecialCourse = CourseManager::isSpecialCourse($courseId);
 
@@ -170,13 +167,16 @@ if (!isset($coursesAlreadyVisited[$course_code])) {
 }
 
 /*Auto launch code */
-$show_autolaunch_lp_warning = false;
-$auto_launch = api_get_course_setting('enable_lp_auto_launch');
+$autoLaunchWarning = '';
+$showAutoLaunchLpWarning = false;
+$course_id = api_get_course_int_id();
+$lpAutoLaunch = api_get_course_setting('enable_lp_auto_launch');
 $session_id = api_get_session_id();
-if (!empty($auto_launch)) {
-    if ($auto_launch == 2) { //LP list
+if (!empty($lpAutoLaunch)) {
+    if ($lpAutoLaunch == 2) {
+        // LP list
         if (api_is_platform_admin() || api_is_allowed_to_edit()) {
-            $show_autolaunch_lp_warning = true;
+            $showAutoLaunchLpWarning = true;
         } else {
             $session_key = 'lp_autolaunch_'.$session_id.'_'.api_get_course_int_id().'_'.api_get_user_id();
             if (!isset($_SESSION[$session_key])) {
@@ -189,7 +189,6 @@ if (!empty($auto_launch)) {
         }
     } else {
         $lp_table = Database::get_course_table(TABLE_LP_MAIN);
-        $course_id = api_get_course_int_id();
         $condition = '';
         if (!empty($session_id)) {
             $condition = api_get_session_condition($session_id);
@@ -211,7 +210,7 @@ if (!empty($auto_launch)) {
             $lp_data = Database::fetch_array($result, 'ASSOC');
             if (!empty($lp_data['id'])) {
                 if (api_is_platform_admin() || api_is_allowed_to_edit()) {
-                    $show_autolaunch_lp_warning = true;
+                    $showAutoLaunchLpWarning = true;
                 } else {
                     $session_key = 'lp_autolaunch_'.$session_id.'_'.api_get_course_int_id().'_'.api_get_user_id();
                     if (!isset($_SESSION[$session_key])) {
@@ -228,22 +227,76 @@ if (!empty($auto_launch)) {
     }
 }
 
+if ($showAutoLaunchLpWarning) {
+    $autoLaunchWarning = get_lang('TheLPAutoLaunchSettingIsONStudentsWillBeRedirectToAnSpecificLP');
+}
+
 $forumAutoLaunch = api_get_course_setting('enable_forum_auto_launch');
 if ($forumAutoLaunch == 1) {
     if (api_is_platform_admin() || api_is_allowed_to_edit()) {
-        Display::addFlash(Display::return_message(
-            get_lang('TheForumAutoLaunchSettingIsOnStudentsWillBeRedirectToTheForumTool'),
-            'warning'
-        ));
+        if (empty($autoLaunchWarning)) {
+            $autoLaunchWarning = get_lang('TheForumAutoLaunchSettingIsOnStudentsWillBeRedirectToTheForumTool');
+        }
     } else {
-        //$forumKey = 'forum_auto_launch_'.$session_id.'_'.api_get_course_int_id().'_'.api_get_user_id();
-        //if (!isset($_SESSION[$forumKey])) {
-        //redirecting to the LP
         $url = api_get_path(WEB_CODE_PATH).'forum/index.php?'.api_get_cidreq().'&id_session='.$session_id;
-        //  $_SESSION[$forumKey] = true;
         header("Location: $url");
         exit;
-        //}
+    }
+}
+
+if (api_get_configuration_value('allow_exercise_auto_launch')) {
+    $exerciseAutoLaunch = (int) api_get_course_setting('enable_exercise_auto_launch');
+    if ($exerciseAutoLaunch == 2) {
+        if (api_is_platform_admin() || api_is_allowed_to_edit()) {
+            if (empty($autoLaunchWarning)) {
+                $autoLaunchWarning = get_lang(
+                    'TheExerciseAutoLaunchSettingIsONStudentsWillBeRedirectToTheExerciseList'
+                );
+            }
+        } else {
+            // Redirecting to the document
+            $url = api_get_path(WEB_CODE_PATH).'exercise/exercise.php?'.api_get_cidreq().'&id_session='.$session_id;
+            header("Location: $url");
+            exit;
+        }
+    } elseif ($exerciseAutoLaunch == 1) {
+        if (api_is_platform_admin() || api_is_allowed_to_edit()) {
+            if (empty($autoLaunchWarning)) {
+                $autoLaunchWarning = get_lang(
+                    'TheExerciseAutoLaunchSettingIsONStudentsWillBeRedirectToAnSpecificExercise'
+                );
+            }
+        } else {
+            // Redirecting to an exercise
+            $table = Database::get_course_table(TABLE_QUIZ_TEST);
+            $sessionCondition = api_get_session_condition($session_id, true);
+            $sql = "SELECT iid FROM $table
+                    WHERE c_id = $course_id AND autolaunch = 1 $sessionCondition
+                    LIMIT 1";
+            $result = Database::query($sql);
+            if (Database::num_rows($result) > 0) {
+                $row = Database::fetch_array($result, 'ASSOC');
+                $exerciseId = $row['iid'];
+                $url = api_get_path(WEB_CODE_PATH).
+                    'exercise/overview.php?exerciseId='.$exerciseId.'&'.api_get_cidreq().'&id_session='.$session_id;
+                header("Location: $url");
+                exit;
+            }
+        }
+    }
+}
+
+$documentAutoLaunch = api_get_course_setting('enable_document_auto_launch');
+if ($documentAutoLaunch == 1) {
+    if (api_is_platform_admin() || api_is_allowed_to_edit()) {
+        if (empty($autoLaunchWarning)) {
+            $autoLaunchWarning = get_lang('TheDocumentAutoLaunchSettingIsOnStudentsWillBeRedirectToTheDocumentTool');
+        }
+    } else {
+        // Redirecting to the document
+        $url = api_get_path(WEB_CODE_PATH).'document/document.php?'.api_get_cidreq().'&id_session='.$session_id;
+        header("Location: $url");
+        exit;
     }
 }
 
@@ -251,8 +304,6 @@ $tool_table = Database::get_course_table(TABLE_TOOL_LIST);
 $temps = time();
 $reqdate = "&reqdate=$temps";
 
-/*	MAIN CODE */
-
 /*	Introduction section (editable by course admins) */
 $content = Display::return_introduction_section(
     TOOL_COURSE_HOMEPAGE,
@@ -266,15 +317,16 @@ $content = Display::return_introduction_section(
 /*	SWITCH TO A DIFFERENT HOMEPAGE VIEW
     the setting homepage_view is adjustable through
     the platform administration section */
-
-if ($show_autolaunch_lp_warning) {
+if (!empty($autoLaunchWarning)) {
     $show_message .= Display::return_message(
-        get_lang('TheLPAutoLaunchSettingIsONStudentsWillBeRedirectToAnSpecificLP'),
+        $autoLaunchWarning,
         'warning'
     );
 }
 
-if (api_get_setting('homepage_view') === 'activity' || api_get_setting('homepage_view') === 'activity_big') {
+if (api_get_setting('homepage_view') === 'activity' ||
+    api_get_setting('homepage_view') === 'activity_big'
+) {
     require 'activity.php';
 } elseif (api_get_setting('homepage_view') === '2column') {
     require '2column.php';

+ 0 - 2
main/course_home/vertical_activity.php

@@ -71,14 +71,12 @@ if (api_is_allowed_to_edit(null, true) && !api_is_coach()) {
     }
 
     $my_list = CourseHome::get_tools_category(TOOL_AUTHORING);
-
     $blocks[] = [
         'title' => get_lang('Authoring'),
         'content' => CourseHome::show_tools_category($my_list),
     ];
 
     $my_list = CourseHome::get_tools_category(TOOL_INTERACTION);
-
     $blocks[] = [
         'title' => get_lang('Interaction'),
         'content' => CourseHome::show_tools_category($my_list),

+ 2 - 1
main/course_info/delete_course.php

@@ -40,7 +40,8 @@ if (isset($_GET['delete']) && $_GET['delete'] === 'yes') {
 } else {
     $message = '<h3>'.get_lang('Course').' : '.$current_course_name.' ('.$current_course_code.') </h3>';
     $message .= '<p>'.get_lang('ByDel').'</p>';
-    $message .= '<p><a class="btn btn-primary" href="'.api_get_path(WEB_CODE_PATH).'course_info/maintenance.php?'.api_get_cidreq().'">'.
+    $message .= '<p><a class="btn btn-primary" 
+        href="'.api_get_path(WEB_CODE_PATH).'course_info/maintenance.php?'.api_get_cidreq().'">'.
         get_lang('No').'</a>&nbsp;<a class="btn btn-danger" href="'.api_get_self().'?delete=yes&'.api_get_cidreq().'">'.
         get_lang('Yes').'</a></p>';
     $interbreadcrumb[] = [

+ 110 - 9
main/course_info/infocours.php

@@ -7,7 +7,6 @@
  *
  * See ./inc/conf/course_info.conf.php for settings
  *
- * @todo Move $canBeEmpty from course_info.conf.php to config-settings
  * @todo Take those config settings into account in this script
  *
  * @author Patrick Cool <patrick.cool@UGent.be>
@@ -175,10 +174,10 @@ if (api_get_setting('allow_course_theme') == 'true') {
 
 $form->addElement('label', get_lang('DocumentQuota'), format_file_size(DocumentManager::get_course_quota()));
 
-if (!empty(ExerciseLib::getScoreModels())) {
-    $models = ExerciseLib::getScoreModels();
+$scoreModels = ExerciseLib::getScoreModels();
+if (!empty($scoreModels)) {
     $options = ['' => get_lang('None')];
-    foreach ($models['models'] as $item) {
+    foreach ($scoreModels['models'] as $item) {
         $options[$item['id']] = get_lang($item['name']);
     }
     $form->addSelect('score_model_id', get_lang('ScoreModel'), $options);
@@ -284,6 +283,17 @@ $group = [
 ];
 $form->addGroup($group, '', [get_lang('ShowSystemFolders')]);
 
+$group = [];
+$group[] = $form->createElement(
+    'radio',
+    'enable_document_auto_launch',
+    get_lang('DocumentAutoLaunch'),
+    get_lang('RedirectToTheDocumentList'),
+    1
+);
+$group[] = $form->createElement('radio', 'enable_document_auto_launch', null, get_lang('Deactivate'), 0);
+$form->addGroup($group, '', [get_lang('DocumentAutoLaunch')]);
+
 $form->addButtonSave(get_lang('SaveSettings'), 'submit_save');
 $form->addHtml('
         </div>
@@ -477,7 +487,7 @@ $group = [];
 $group[] = $form->createElement('radio', 'enable_lp_auto_launch', get_lang('LPAutoLaunch'), get_lang('RedirectToALearningPath'), 1);
 $group[] = $form->createElement('radio', 'enable_lp_auto_launch', get_lang('LPAutoLaunch'), get_lang('RedirectToTheLearningPathList'), 2);
 $group[] = $form->createElement('radio', 'enable_lp_auto_launch', null, get_lang('Deactivate'), 0);
-$form->addGroup($group, '', [get_lang("LPAutoLaunch")]);
+$form->addGroup($group, '', [get_lang('LPAutoLaunch')]);
 
 if (api_get_setting('allow_course_theme') == 'true') {
     // Allow theme into Learning path
@@ -555,6 +565,73 @@ $form->addHtml('
 ');
 $form->addHtml('</div>');
 
+// Exercise
+if (api_get_configuration_value('allow_exercise_auto_launch')) {
+    $form->addHtml('<div class="panel panel-default">');
+    $form->addHtml(
+        '
+        <div class="panel-heading" role="tab" id="heading-exercise">
+            <h4 class="panel-title">
+                <a class="collapsed" 
+                role="button" data-toggle="collapse" 
+                data-parent="#accordion" 
+                href="#collapse-exercise" aria-expanded="false" aria-controls="collapse-exercise">
+    '
+    );
+    $form->addHtml(
+        Display::return_icon('quiz.png', get_lang('Exercise')).' '.get_lang('Exercise')
+    );
+    $form->addHtml(
+        '
+                </a>
+            </h4>
+        </div>
+    '
+    );
+    $form->addHtml(
+        '
+        <div id="collapse-exercise" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading-exercise">
+            <div class="panel-body">
+    '
+    );
+
+    // Auto launch exercise
+    $group = [];
+    $group[] = $form->createElement(
+        'radio',
+        'enable_exercise_auto_launch',
+        get_lang('ExerciseAutoLaunch'),
+        get_lang('RedirectToExercise'),
+        1
+    );
+    $group[] = $form->createElement(
+        'radio',
+        'enable_exercise_auto_launch',
+        get_lang('ExerciseAutoLaunch'),
+        get_lang('RedirectToTheExerciseList'),
+        2
+    );
+    $group[] = $form->createElement('radio', 'enable_exercise_auto_launch', null, get_lang('Deactivate'), 0);
+    $form->addGroup($group, '', [get_lang('ExerciseAutoLaunch')]);
+
+    if (is_settings_editable()) {
+        $form->addButtonSave(get_lang('SaveSettings'), 'submit_save');
+    } else {
+        // Is it allowed to edit the course settings?
+        if (!is_settings_editable()) {
+            $disabled_output = "disabled";
+        }
+        $form->freeze();
+    }
+    $form->addHtml(
+        '
+            </div>
+        </div>
+    '
+    );
+    $form->addHtml('</div>');
+}
+
 // THEMATIC ADVANCE SETTINGS
 $form->addHtml('<div class="panel panel-default">');
 $form->addHtml('
@@ -579,10 +656,34 @@ $form->addHtml('
 ');
 
 $group = [];
-$group[] = $form->createElement('radio', 'display_info_advance_inside_homecourse', get_lang('InfoAboutAdvanceInsideHomeCourse'), get_lang('DisplayAboutLastDoneAdvance'), 1);
-$group[] = $form->createElement('radio', 'display_info_advance_inside_homecourse', null, get_lang('DisplayAboutNextAdvanceNotDone'), 2);
-$group[] = $form->createElement('radio', 'display_info_advance_inside_homecourse', null, get_lang('DisplayAboutNextAdvanceNotDoneAndLastDoneAdvance'), 3);
-$group[] = $form->createElement('radio', 'display_info_advance_inside_homecourse', null, get_lang('DoNotDisplayAnyAdvance'), 0);
+$group[] = $form->createElement(
+    'radio',
+    'display_info_advance_inside_homecourse',
+    get_lang('InfoAboutAdvanceInsideHomeCourse'),
+    get_lang('DisplayAboutLastDoneAdvance'),
+    1
+);
+$group[] = $form->createElement(
+    'radio',
+    'display_info_advance_inside_homecourse',
+    null,
+    get_lang('DisplayAboutNextAdvanceNotDone'),
+    2
+);
+$group[] = $form->createElement(
+    'radio',
+    'display_info_advance_inside_homecourse',
+    null,
+    get_lang('DisplayAboutNextAdvanceNotDoneAndLastDoneAdvance'),
+    3
+);
+$group[] = $form->createElement(
+    'radio',
+    'display_info_advance_inside_homecourse',
+    null,
+    get_lang('DoNotDisplayAnyAdvance'),
+    0
+);
 $form->addGroup($group, '', [get_lang("InfoAboutAdvanceInsideHomeCourse")]);
 $form->addButtonSave(get_lang('SaveSettings'), 'submit_save');
 $form->addHtml('

+ 39 - 11
main/course_info/maintenance.php

@@ -13,7 +13,12 @@ $nameTools = get_lang('Maintenance');
 api_protect_course_script(true);
 api_block_anonymous_users();
 
-Display :: display_header($nameTools);
+// Check access rights (only teachers are allowed here)
+if (!api_is_allowed_to_edit()) {
+    api_not_allowed(true);
+}
+
+Display::display_header($nameTools);
 echo Display::page_header($nameTools);
 
 ?>
@@ -24,27 +29,50 @@ echo Display::page_header($nameTools);
 </div>
 <div class="sectioncomment">
     <ul>
-        <li><a href="../coursecopy/create_backup.php?<?php echo api_get_cidreq(); ?>"><?php echo get_lang('CreateBackup'); ?></a><br/>
-        <?php echo get_lang('CreateBackupInfo'); ?>
+        <li>
+            <a href="../coursecopy/create_backup.php?<?php echo api_get_cidreq(); ?>">
+                <?php echo get_lang('CreateBackup'); ?>
+            </a><br/>
+            <?php echo get_lang('CreateBackupInfo'); ?>
         </li>
-        <li><a href="../coursecopy/import_backup.php?<?php echo api_get_cidreq(); ?>"><?php echo get_lang('ImportBackup'); ?></a><br/>
-        <?php echo get_lang('ImportBackupInfo'); ?>
+        <li>
+            <a href="../coursecopy/import_backup.php?<?php echo api_get_cidreq(); ?>">
+                <?php echo get_lang('ImportBackup'); ?>
+            </a><br/>
+            <?php echo get_lang('ImportBackupInfo'); ?>
         </li>
-        <li><a href="../coursecopy/import_moodle.php?<?php echo api_get_cidreq(); ?>"><?php echo get_lang('ImportFromMoodle'); ?></a><br/>
+        <li>
+            <a href="../coursecopy/import_moodle.php?<?php echo api_get_cidreq(); ?>">
+                <?php echo get_lang('ImportFromMoodle'); ?>
+            </a><br/>
             <?php echo get_lang('ImportFromMoodleInfo'); ?>
         </li>
     </ul>
 </div>
 
-<div class="sectiontitle"><?php Display::display_icon('copy.gif', get_lang('CopyCourse')); ?>&nbsp;&nbsp;<a href="../coursecopy/copy_course.php?<?php echo api_get_cidreq(); ?>"><?php echo get_lang('CopyCourse'); ?></a></div>
-<div class="sectioncomment"><?php echo get_lang('DescriptionCopyCourse'); ?></div>
+<div class="sectiontitle">
+    <?php Display::display_icon('copy.gif', get_lang('CopyCourse')); ?>&nbsp;&nbsp;
+    <a href="../coursecopy/copy_course.php?<?php echo api_get_cidreq(); ?>">
+        <?php echo get_lang('CopyCourse'); ?></a>
+</div>
+<div class="sectioncomment"><?php echo get_lang('DescriptionCopyCourse'); ?>
+</div>
 
-<div class="sectiontitle"><?php Display::display_icon('tool_delete.gif', get_lang('recycle_course')); ?>&nbsp;&nbsp;<a href="../coursecopy/recycle_course.php?<?php echo api_get_cidreq(); ?>"><?php echo get_lang('recycle_course'); ?></a></div>
+<div class="sectiontitle">
+    <?php Display::display_icon('tool_delete.gif', get_lang('recycle_course')); ?>&nbsp;&nbsp;
+    <a href="../coursecopy/recycle_course.php?<?php echo api_get_cidreq(); ?>">
+        <?php echo get_lang('recycle_course'); ?>
+    </a>
+</div>
 <div class="sectioncomment"><?php echo get_lang('DescriptionRecycleCourse'); ?></div>
 
-<div class="sectiontitle"><?php Display::display_icon('delete.gif', get_lang('DelCourse')); ?>&nbsp;&nbsp;<a href="../course_info/delete_course.php?<?php echo api_get_cidreq(); ?>"><?php echo get_lang('DelCourse'); ?></a></div>
+<div class="sectiontitle">
+    <?php Display::display_icon('delete.gif', get_lang('DelCourse')); ?>&nbsp;&nbsp;
+    <a href="../course_info/delete_course.php?<?php echo api_get_cidreq(); ?>"><?php echo get_lang('DelCourse'); ?>
+    </a>
+</div>
 <div class="sectioncomment"><?php echo get_lang('DescriptionDeleteCourse'); ?></div>
 
 <?php
-// Footer
+
 Display::display_footer();

+ 1 - 1
main/course_info/maintenance_coach.php

@@ -23,7 +23,7 @@ if ($sessionsCopy !== 'true') {
     api_not_allowed(true);
 }
 
-Display :: display_header($nameTools);
+Display::display_header($nameTools);
 
 echo Display::page_subheader(
     Display::return_icon(

+ 7 - 2
main/course_info/tools.php

@@ -56,9 +56,14 @@ switch ($action) {
         $form->addHtml('<div class="col-md-7">');
         $form->addText('name', get_lang('Name'));
         $form->addText('link', get_lang('Links'));
-        $allowed_picture_types = ['jpg', 'jpeg', 'png'];
+        $allowedPictureTypes = ['jpg', 'jpeg', 'png'];
         $form->addFile('icon', get_lang('CustomIcon'));
-        $form->addRule('icon', get_lang('OnlyImagesAllowed').' ('.implode(',', $allowed_picture_types).')', 'filetype', $allowed_picture_types);
+        $form->addRule(
+            'icon',
+            get_lang('OnlyImagesAllowed').' ('.implode(',', $allowedPictureTypes).')',
+            'filetype',
+            $allowedPictureTypes
+        );
         $form->addSelect(
             'target',
             get_lang('LinkTarget'),

+ 0 - 1
main/course_progress/index.php

@@ -18,7 +18,6 @@ require_once 'thematic_controller.php';
 
 // current section
 $this_section = SECTION_COURSES;
-
 $current_course_tool = TOOL_COURSE_PROGRESS;
 
 // protect a course script

+ 2 - 2
main/course_progress/layout.php

@@ -14,7 +14,7 @@ api_protect_course_script(true);
 
 // Header
 $tool = TOOL_COURSE_PROGRESS;
-Display :: display_header('');
+Display::display_header('');
 
 // Introduction section
 Display::display_introduction_section($tool);
@@ -26,4 +26,4 @@ Event::event_access_tool($tool);
 echo $content;
 
 // Footer
-Display :: display_footer();
+Display::display_footer();

+ 1 - 2
main/course_progress/thematic.php

@@ -129,7 +129,6 @@ if ($action == 'thematic_list') {
             $list['content'] = Security::remove_XSS($thematic['content'], STUDENT);
             $list['display_orden'] = $thematic['display_order'];
             $list['active'] = $thematic['active'];
-
             $my_thematic_id = $thematic['id'];
 
             $session_star = '';
@@ -248,7 +247,7 @@ if ($action == 'thematic_list') {
         get_lang('Content'),
         false,
         false,
-        ['ToolbarSet' => 'TrainingDescription', 'Height' => '150']
+        ['ToolbarSet' => 'Basic', 'Height' => '150']
     );
     $form->addButtonSave(get_lang('Save'));
 

+ 1 - 1
main/course_progress/thematic_advance.php

@@ -126,7 +126,7 @@ if ($action === 'thematic_advance_add' || $action === 'thematic_advance_edit') {
         false,
         [
             'ToolbarStartExpanded' => 'false',
-            'ToolbarSet' => 'TrainingDescription',
+            'ToolbarSet' => 'Basic',
             'Height' => '150',
         ]
     );

+ 2 - 2
main/course_progress/thematic_controller.php

@@ -85,13 +85,13 @@ class ThematicController
                     }
                     break;
                 case 'thematic_copy':
-                    //Copy a thematic to a session
+                    // Copy a thematic to a session
                     $thematic->copy($thematic_id);
                     $thematic_id = null;
                     $action = 'thematic_details';
                     break;
                 case 'thematic_delete_select':
-                    //Delete many thematics
+                    // Delete many thematics
                     if (strtoupper($_SERVER['REQUEST_METHOD']) == "POST") {
                         if (api_is_allowed_to_edit(null, true)) {
                             $thematic_ids = $_POST['id'];

+ 2 - 2
main/course_progress/thematic_plan.php

@@ -69,7 +69,7 @@ if ($action === 'thematic_plan_list') {
             false,
             [
                 'ToolbarStartExpanded' => 'false',
-                'ToolbarSet' => 'TrainingDescription',
+                'ToolbarSet' => 'Basic',
                 'Height' => '150',
             ]
         );
@@ -138,7 +138,7 @@ if ($action === 'thematic_plan_list') {
         false,
         [
             'ToolbarStartExpanded' => 'false',
-            'ToolbarSet' => 'TrainingDescription',
+            'ToolbarSet' => 'Basic',
             'Width' => '80%',
             'Height' => '150',
         ]

+ 8 - 3
main/coursecopy/copy_course_session.php

@@ -111,7 +111,13 @@ function display_form()
 
     // origin
     $html .= '<label class="col-sm-2 control-label">'.get_lang('OriginCoursesFromSession').': </label>';
-    $html .= '<div class="col-sm-5">'.make_select_session_list('sessions_list_origin', $sessions, ['onchange' => 'javascript: xajax_search_courses(this.value,\'origin\');']).'</div>';
+    $html .= '<div class="col-sm-5">';
+    $html .= make_select_session_list(
+        'sessions_list_origin',
+        $sessions,
+        ['onchange' => 'javascript: xajax_search_courses(this.value,\'origin\');']
+    );
+    $html .= '</div>';
     $html .= '<div class="col-sm-5" id="ajax_list_courses_origin">';
     $html .= '<select id="origin" class="form-control" name="SessionCoursesListOrigin[]" ></select>';
     $html .= '</div></div>';
@@ -155,7 +161,7 @@ function display_form()
  */
 function search_courses($id_session, $type)
 {
-    global $tbl_course, $tbl_session_rel_course, $course_list;
+    global $tbl_course, $tbl_session_rel_course;
     $xajax_response = new xajaxResponse();
     $select_destination = '';
     $return = null;
@@ -179,7 +185,6 @@ function search_courses($id_session, $type)
             // Build select for destination sessions where is not included current session from select origin
             if (!empty($id_session)) {
                 $sessions = SessionManager::get_sessions_list([], ['name', 'ASC']);
-
                 $select_destination .= '<select name="sessions_list_destination" class="form-control" onchange = "javascript: xajax_search_courses(this.value,\'destination\');">';
                 $select_destination .= '<option value = "0">-- '.get_lang('SelectASession').' --</option>';
                 foreach ($sessions as $session) {

+ 1 - 1
main/cron/fix_course_spent_time.php

@@ -44,7 +44,7 @@ while ($row = Database::fetch_array($result)) {
         $newLogout = api_get_utc_datetime($login);
         $sql = "UPDATE track_e_course_access 
                 SET logout_course_date = '$newLogout'
-                WHERE course_access_id = $id
+                WHERE course_access_id = $id ;
         ";
 
         // Uncomment to fix

+ 3 - 2
main/document/create_audio.php

@@ -223,7 +223,6 @@ while ($row = Database::fetch_array($result_select)) {
 
 if ($service == 'google') {
     $selected_language = api_get_language_isocode(); //lang default is the course language
-
     $form = new FormValidator('form1', 'post', api_get_self().'?'.api_get_cidreq(), '', ['id' => 'form1']);
     $form->addHeader(get_lang('HelpText2Audio'));
     $form->addElement('hidden', 'text2voice_mode', 'google');
@@ -255,7 +254,9 @@ function downloadAudioGoogle($filepath, $dir)
     $location = 'create_audio.php?'.api_get_cidreq().'&id='.intval($_POST['id']).'&service=google';
 
     //security
-    if (!isset($_POST['lang']) && !isset($_POST['text']) && !isset($_POST['title']) && !isset($filepath) && !isset($dir)) {
+    if (!isset($_POST['lang']) && !isset($_POST['text']) &&
+        !isset($_POST['title']) && !isset($filepath) && !isset($dir)
+    ) {
         echo '<script>window.location.href="'.$location.'"</script>';
 
         return;

+ 4 - 3
main/document/create_document.php

@@ -10,6 +10,9 @@ use ChamiloSession as Session;
  */
 require_once __DIR__.'/../inc/global.inc.php';
 
+api_protect_course_script();
+api_protect_course_group(GroupManager::GROUP_TOOL_DOCUMENTS);
+
 $this_section = SECTION_COURSES;
 $groupRights = Session::read('group_member_with_upload_rights');
 $htmlHeadXtra[] = '
@@ -326,8 +329,7 @@ $form->addHtmlEditor(
     get_lang('Content'),
     true,
     true,
-    $editorConfig,
-    true
+    $editorConfig
 );
 
 // Comment-field
@@ -631,7 +633,6 @@ if ($form->validate()) {
 
     Display :: display_header($nameTools, "Doc");
     // actions
-
     // link back to the documents overview
     if ($is_certificate_mode) {
         $actionsLeft = '<a href="document.php?certificate=true&id='.$folder_id.'&selectcat='.Security::remove_XSS($_GET['selectcat']).'">'.

+ 1 - 0
main/document/create_draw.php

@@ -20,6 +20,7 @@ $nameTools = get_lang('Draw');
 
 api_protect_course_script();
 api_block_anonymous_users();
+api_protect_course_group(GroupManager::GROUP_TOOL_DOCUMENTS);
 
 $document_data = DocumentManager::get_document_data_by_id(
     $_GET['id'],

+ 57 - 20
main/document/document.php

@@ -152,12 +152,12 @@ $groupMemberWithEditRights = false;
 if (!empty($groupId)) {
     $group_properties = GroupManager::get_group_properties($groupId);
     $groupIid = isset($group_properties['iid']) ? $group_properties['iid'] : 0;
-    $isTutorGroup = GroupManager::is_tutor_of_group(
+    $groupMemberWithEditRights = GroupManager::allowUploadEditDocument(
         $userId,
+        $courseId,
         $group_properties,
-        $courseId
+        null
     );
-    $groupMemberWithEditRights = $isAllowedToEdit || $isTutorGroup;
 
     // Let's assume the user cannot upload files for the group
     $groupMemberWithUploadRights = false;
@@ -198,6 +198,11 @@ if (!empty($groupId)) {
             $groupMemberWithUploadRights = true;
         }
     }
+
+    // Group mode
+    if (!GroupManager::allowUploadEditDocument($userId, $courseId, $group_properties)) {
+        $groupMemberWithUploadRights = false;
+    }
     Session::write('group_member_with_upload_rights', $groupMemberWithUploadRights);
 } else {
     Session::write('group_member_with_upload_rights', false);
@@ -241,6 +246,14 @@ switch ($action) {
                     $sessionId
                 );
 
+                GroupManager::allowUploadEditDocument(
+                    $userId,
+                    $courseId,
+                    $group_properties,
+                    $documentInfo,
+                    true
+                );
+
                 // Check whether the document is in the database.
                 if (!empty($documentInfo)) {
                     if ($documentInfo['filetype'] != 'link') {
@@ -331,9 +344,9 @@ switch ($action) {
         exit;
         break;
     case 'downloadfolder':
-        if (api_get_setting('students_download_folders') == 'true'
-            || $isAllowedToEdit
-            || api_is_platform_admin()
+        if (api_get_setting('students_download_folders') == 'true' ||
+            $isAllowedToEdit ||
+            api_is_platform_admin()
         ) {
             // Get the document data from the ID
             $document_data = DocumentManager::get_document_data_by_id(
@@ -416,6 +429,15 @@ switch ($action) {
                     0
                 );
             }
+
+            GroupManager::allowUploadEditDocument(
+                $userId,
+                $courseId,
+                $group_properties,
+                $document_info,
+                true
+            );
+
             $parent_id = $document_info['parent_id'];
             $my_path = UserManager::getUserPathById(api_get_user_id(), 'system');
             $user_folder = $my_path.'my_files/';
@@ -861,14 +883,6 @@ if (isset($_GET['createdir'])) {
     $interbreadcrumb[] = ['url' => '#', 'name' => get_lang('CreateDir')];
 }
 
-$js_path = api_get_path(WEB_LIBRARY_PATH).'javascript/';
-
-$htmlHeadXtra[] = '<link rel="stylesheet" href="'.$js_path
-    .'jquery-jplayer/skin/chamilo/jplayer.blue.monday.css" type="text/css">';
-$htmlHeadXtra[] = '<script type="text/javascript" src="'.$js_path
-    .'jquery-jplayer/jplayer/jquery.jplayer.min.js"></script>';
-$mediaplayer_path = api_get_path(WEB_LIBRARY_PATH).'mediaplayer/player.swf';
-
 $documentAndFolders = DocumentManager::getAllDocumentData(
     $courseInfo,
     $curdirpath,
@@ -920,7 +934,7 @@ if (!empty($documentAndFolders)) {
                     'extension' => $extension,
                     'count' => $count,
                 ];
-                $jquery .= DocumentManager::generate_jplayer_jquery($params);
+                $jquery .= DocumentManager::generateAudioJavascript($params);
                 $count++;
             }
         }
@@ -973,6 +987,14 @@ if ($isAllowedToEdit || $groupMemberWithUploadRights ||
             $sessionId
         );
 
+        GroupManager::allowUploadEditDocument(
+            $userId,
+            $courseId,
+            $group_properties,
+            $document_to_move,
+            true
+        );
+
         $move_path = $document_to_move['path'];
         if (!empty($document_to_move)) {
             $folders = DocumentManager::get_all_document_folders(
@@ -1038,6 +1060,14 @@ if ($isAllowedToEdit || $groupMemberWithUploadRights ||
             $sessionId
         );
 
+        GroupManager::allowUploadEditDocument(
+            $userId,
+            $courseId,
+            $group_properties,
+            $document_to_move,
+            true
+        );
+
         // Security fix: make sure they can't move files that are not in the document table
         if (!empty($document_to_move)) {
             if ($document_to_move['filetype'] == 'link') {
@@ -1783,6 +1813,7 @@ $userIsSubscribed = CourseManager::is_user_subscribed_in_course(
 );
 
 $getSizeURL = api_get_path(WEB_AJAX_PATH).'document.ajax.php?a=get_dir_size&'.api_get_cidreq();
+
 if (!empty($documentAndFolders)) {
     if ($groupId == 0 || $userAccess) {
         $count = 1;
@@ -1845,8 +1876,8 @@ if (!empty($documentAndFolders)) {
 
             // Icons (clickable)
             $row[] = DocumentManager::create_document_link(
+                $http_www,
                 $document_data,
-                $courseInfo,
                 true,
                 $count,
                 $is_visible,
@@ -1866,8 +1897,8 @@ if (!empty($documentAndFolders)) {
             $session_img = api_get_session_image($document_data['session_id'], $_user['status']);
 
             $link = DocumentManager::create_document_link(
+                $http_www,
                 $document_data,
-                $courseInfo,
                 false,
                 null,
                 $is_visible,
@@ -1901,11 +1932,17 @@ if (!empty($documentAndFolders)) {
 
             $row[] = $invisibility_span_open.$display_date.$invisibility_span_close;
 
+            $groupMemberWithEditRightsCheckDocument = GroupManager::allowUploadEditDocument(
+                $userId,
+                $courseId,
+                $group_properties,
+                $document_data
+            );
+
             // Admins get an edit column
             if ($isAllowedToEdit ||
-                $groupMemberWithEditRights ||
-                DocumentManager::is_my_shared_folder(api_get_user_id(), $curdirpath, $sessionId) ||
-                $document_data['insert_user_id'] == api_get_user_id()
+                $groupMemberWithEditRightsCheckDocument ||
+                DocumentManager::is_my_shared_folder(api_get_user_id(), $curdirpath, $sessionId)
             ) {
                 $is_template = isset($document_data['is_template']) ? $document_data['is_template'] : false;
 

+ 32 - 22
main/document/edit_document.php

@@ -58,11 +58,6 @@ $lib_path = api_get_path(LIBRARY_PATH);
 $course_info = api_get_course_info();
 $group_id = api_get_group_id();
 $sessionId = api_get_session_id();
-
-if (api_is_in_group()) {
-    $group_properties = GroupManager::get_group_properties($group_id);
-}
-
 $dir = '/';
 $currentDirPath = isset($_GET['curdirpath']) ? Security::remove_XSS($_GET['curdirpath']) : null;
 $readonly = false;
@@ -97,6 +92,10 @@ if (empty($document_data)) {
     api_not_allowed(true);
 }
 
+if (api_is_in_group()) {
+    $group_properties = GroupManager::get_group_properties($group_id);
+}
+
 $is_certificate_mode = DocumentManager::is_certificate_mode($dir);
 
 //Call from
@@ -192,6 +191,34 @@ if (!api_is_allowed_to_edit()) {
     }
 }
 
+$document_info = api_get_item_property_info(
+    api_get_course_int_id(),
+    'document',
+    $document_id,
+    0
+);
+
+// Try to find this document in the session
+if (!empty($sessionId)) {
+    $document_info = api_get_item_property_info(
+        api_get_course_int_id(),
+        'document',
+        $document_id,
+        $sessionId
+    );
+}
+
+if (api_is_in_group()) {
+    $group_properties = GroupManager::get_group_properties($group_id);
+    GroupManager::allowUploadEditDocument(
+        api_get_user_id(),
+        api_get_course_int_id(),
+        $group_properties,
+        $document_info,
+        true
+    );
+}
+
 /* MAIN TOOL CODE */
 /* Code to change the comment */
 if (isset($_POST['comment'])) {
@@ -327,23 +354,6 @@ if (file_exists($document_data['absolute_path'])) {
 $nameTools = get_lang('EditDocument').': '.Security::remove_XSS($document_data['title']);
 Display::display_header($nameTools, 'Doc');
 
-$document_info = api_get_item_property_info(
-    api_get_course_int_id(),
-    'document',
-    $document_id,
-    0
-);
-
-// Try to find this document in the session
-if (!empty($sessionId)) {
-    $document_info = api_get_item_property_info(
-        api_get_course_int_id(),
-        'document',
-        $document_id,
-        $sessionId
-    );
-}
-
 $owner_id = $document_info['insert_user_id'];
 $last_edit_date = $document_info['lastedit_date'];
 $createdDate = $document_info['insert_date'];

+ 1 - 0
main/document/record_audio.php

@@ -15,6 +15,7 @@ $nameTools = get_lang('VoiceRecord');
 
 api_protect_course_script();
 api_block_anonymous_users();
+api_protect_course_group(GroupManager::GROUP_TOOL_DOCUMENTS);
 
 $document_data = DocumentManager::get_document_data_by_id(
     $_GET['id'],

+ 8 - 64
main/document/showinframes.php

@@ -102,11 +102,10 @@ if (!$is_allowed_to_edit && !$is_visible) {
 }
 
 $pathinfo = pathinfo($header_file);
-$jplayer_supported_files = ['mp4', 'ogv', 'flv', 'm4v', 'webm'];
-$jplayer_supported = false;
-
-if (in_array(strtolower($pathinfo['extension']), $jplayer_supported_files)) {
-    $jplayer_supported = true;
+$playerSupportedFiles = ['mp4', 'ogv', 'flv', 'm4v', 'webm'];
+$playerSupported = false;
+if (in_array(strtolower($pathinfo['extension']), $playerSupportedFiles)) {
+    $playerSupported = true;
 }
 
 $group_id = api_get_group_id();
@@ -195,21 +194,6 @@ $web_odf_supported_files = DocumentManager::get_web_odf_extension_list();
 $web_odf_supported_files[] = 'pdf';
 if (in_array(strtolower($pathinfo['extension']), $web_odf_supported_files)) {
     $show_web_odf = true;
-    /*
-    $htmlHeadXtra[] = api_get_js('webodf/webodf.js');
-    $htmlHeadXtra[] = api_get_css(api_get_path(WEB_LIBRARY_PATH).'javascript/webodf/webodf.css');
-    $htmlHeadXtra[] = '
-    <script charset="utf-8">
-        function init() {
-                var odfelement = document.getElementById("odf"),
-                odfcanvas = new odf.OdfCanvas(odfelement);
-                odfcanvas.load("'.$file_url_web.'");
-        }
-        $(document).ready(function() {
-            window.setTimeout(init, 0);
-        });
-  </script>';
-    */
     $htmlHeadXtra[] = '
     <script>
         resizeIframe = function() {
@@ -246,45 +230,8 @@ if ($isChatFolder) {
 }
 
 $execute_iframe = true;
-if ($jplayer_supported) {
+if ($playerSupported) {
     $extension = api_strtolower($pathinfo['extension']);
-    if ($extension == 'mp4') {
-        $extension = 'm4v';
-    }
-    if ($extension == 'webm') {
-        $extension = 'webmv';
-    }
-    $js_path = api_get_path(WEB_LIBRARY_PATH).'javascript/';
-    $htmlHeadXtra[] = '<link rel="stylesheet" href="'.$js_path.'jquery-jplayer/skin/blue.monday/css/jplayer.blue.monday.css" type="text/css">';
-    $htmlHeadXtra[] = '<script type="text/javascript" src="'.$js_path.'jquery-jplayer/jplayer/jquery.jplayer.min.js"></script>';
-
-    $jquery = '
-        $("#jquery_jplayer_1").jPlayer({
-            ready: function() {
-                $(this).jPlayer("setMedia", {
-                    '.$extension.' : "'.$document_data['direct_url'].'"
-                });
-            },
-            cssSelectorAncestor: "#jp_container_1",
-            swfPath: "'.$js_path.'jquery-jplayer/jplayer/",
-            supplied: "'.$extension.'",
-            useStateClassSkin: true,
-            autoBlur: false,
-            keyEnabled: false,
-            remainingDuration: true,
-            toggleDuration: true,
-            solution: "html, flash",
-            errorAlerts: false,
-            warningAlerts: false
-        });
-    ';
-
-    $htmlHeadXtra[] = '<script>
-        $(document).ready( function() {
-            //Experimental changes to preview mp3, ogg files
-        '.$jquery.'
-        });
-    </script>';
     $execute_iframe = false;
 }
 
@@ -297,7 +244,7 @@ if ($is_freemind_available) {
     $execute_iframe = false;
 }
 
-if (!$jplayer_supported && $execute_iframe) {
+if (!$playerSupported && $execute_iframe) {
     $htmlHeadXtra[] = '<script>
     <!--
         var jQueryFrameReadyConfigPath = \''.api_get_jquery_web_path().'\';
@@ -349,11 +296,8 @@ if ($show_web_odf) {
 
 echo '</div>';
 
-if ($jplayer_supported) {
-    echo DocumentManager::generate_video_preview($document_data);
-
-    // media_element blocks jplayer disable it
-    Display::$global_template->assign('show_media_element', 0);
+if ($playerSupported) {
+    echo DocumentManager::generateVideoPreview($file_url_web, $extension);
 }
 
 if ($is_freemind_available) {

+ 1 - 1
main/document/upload.php

@@ -85,7 +85,6 @@ if (empty($document_data)) {
 $group_properties = [];
 
 $htmlHeadXtra[] = '<script>
-
 function check_unzip() {
     if (document.upload.unzip.checked){
         //document.upload.if_exists[0].disabled=true;
@@ -120,6 +119,7 @@ if (!empty($groupId)) {
     } else {
         api_not_allowed(true);
     }
+    GroupManager::allowUploadEditDocument(api_get_user_id(), api_get_course_int_id(), $group_properties, null, true);
 } elseif ($is_allowed_to_edit ||
     DocumentManager::is_my_shared_folder(api_get_user_id(), $path, api_get_session_id())) {
 } else {

+ 60 - 179
main/document/webcam_clip.php

@@ -8,41 +8,43 @@ use ChamiloSession as Session;
  *
  * @package chamilo.document
  *
- * @author Juan Carlos Raña Trabado herodoto@telefonica.net
+ * @author  Juan Carlos Raña Trabado herodoto@telefonica.net
  *
- * @since 7/jun/2012
+ * @since   7/jun/2012
  * @Updated 04/09/2015 Upgrade to WebCamJS
  */
 require_once __DIR__.'/../inc/global.inc.php';
 
+$_SESSION['whereami'] = 'document/webcamclip';
 $this_section = SECTION_COURSES;
 $nameTools = get_lang('WebCamClip');
+$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PATH).'web/assets/webcamjs/webcam.js');
+$htmlHeadXtra[] = api_get_js('webcam_recorder.js');
 $groupRights = Session::read('group_member_with_upload_rights');
 
 api_protect_course_script();
 api_block_anonymous_users();
 
-$document_data = DocumentManager::get_document_data_by_id(
-    $_GET['id'],
-    api_get_course_id(),
-    true
-);
-if (empty($document_data)) {
+$userId = api_get_user_id();
+$courseCode = api_get_course_id();
+$groupId = api_get_group_id();
+$sessionId = api_get_session_id();
+
+$documentData = DocumentManager::get_document_data_by_id($_GET['id'], $courseCode, true);
+
+if (empty($documentData)) {
     if (api_is_in_group()) {
-        $group_properties = GroupManager::get_group_properties(api_get_group_id());
-        $document_id = DocumentManager::get_document_id(
+        $groupProperties = GroupManager::get_group_properties($groupId);
+        $documentId = DocumentManager::get_document_id(
             api_get_course_info(),
-            $group_properties['directory']
-        );
-        $document_data = DocumentManager::get_document_data_by_id(
-            $document_id,
-            api_get_course_id()
+            $groupProperties['directory']
         );
+        $documentData = DocumentManager::get_document_data_by_id($documentId, $courseCode);
     }
 }
 
-$document_id = $document_data['id'];
-$dir = $document_data['path'];
+$documentId = $documentData['id'];
+$dir = $documentData['path'];
 
 //make some vars
 $webcamdir = $dir;
@@ -50,7 +52,7 @@ if ($webcamdir == "/") {
     $webcamdir = '';
 }
 
-$is_allowed_to_edit = api_is_allowed_to_edit(null, true);
+$isAllowedToEdit = api_is_allowed_to_edit(null, true);
 
 // Please, do not modify this dirname formatting
 if (strstr($dir, '..')) {
@@ -76,14 +78,13 @@ if (!is_dir($filepath)) {
     $dir = '/';
 }
 
-$groupId = api_get_group_id();
-
 if (!empty($groupId)) {
     $interbreadcrumb[] = [
         "url" => "../group/group_space.php?".api_get_cidreq(),
         "name" => get_lang('GroupSpace'),
     ];
-    $group = GroupManager :: get_group_properties($groupId);
+    $noPHP_SELF = true;
+    $group = GroupManager::get_group_properties($groupId);
     $path = explode('/', $dir);
     if ('/'.$path[1] != $group['directory']) {
         api_not_allowed(true);
@@ -91,7 +92,7 @@ if (!empty($groupId)) {
 }
 
 $interbreadcrumb[] = [
-    "url" => "./document.php?id=".$document_id."&".api_get_cidreq(),
+    "url" => "./document.php?id=".$documentId."&".api_get_cidreq(),
     "name" => get_lang('Documents'),
 ];
 
@@ -99,186 +100,66 @@ if (!api_is_allowed_in_course()) {
     api_not_allowed(true);
 }
 
-if (!($is_allowed_to_edit || $groupRights ||
-    DocumentManager::is_my_shared_folder(api_get_user_id(), Security::remove_XSS($dir), api_get_session_id()))) {
+$isMySharedFolder = DocumentManager::is_my_shared_folder($userId, Security::remove_XSS($dir), $sessionId);
+
+if (!($isAllowedToEdit || $groupRights || $isMySharedFolder)) {
     api_not_allowed(true);
 }
 
 /*	Header */
 Event::event_access_tool(TOOL_DOCUMENT);
 
-$display_dir = $dir;
+$displayDir = $dir;
 if (isset($group)) {
-    $display_dir = explode('/', $dir);
-    unset($display_dir[0]);
-    unset($display_dir[1]);
-    $display_dir = implode('/', $display_dir);
+    $displayDir = explode('/', $dir);
+    unset($displayDir[0]);
+    unset($displayDir[1]);
+    $displayDir = implode('/', $displayDir);
 }
 
 // Interbreadcrumb for the current directory root path
 $counter = 0;
-if (isset($document_data['parents'])) {
-    foreach ($document_data['parents'] as $document_sub_data) {
+if (isset($documentData['parents'])) {
+    foreach ($documentData['parents'] as $documentSubData) {
         //fixing double group folder in breadcrumb
-        if (api_get_group_id()) {
+        if ($groupId) {
             if ($counter == 0) {
                 $counter++;
                 continue;
             }
         }
         $interbreadcrumb[] = [
-            'url' => $document_sub_data['document_url'],
-            'name' => $document_sub_data['title'],
+            'url' => $documentSubData['document_url'],
+            'name' => $documentSubData['title'],
         ];
         $counter++;
     }
 }
 
-//make some vars
-$webcamuserid = api_get_user_id();
-
-Display :: display_header($nameTools, 'Doc');
-echo '<div class="actions">';
-echo '<a href="document.php?id='.$document_id.'">'.
-    Display::return_icon(
-        'back.png',
-        get_lang('BackTo').' '.get_lang('DocumentsOverview'),
-        '',
-        ICON_SIZE_MEDIUM
-    )
-    .'</a>';
-echo '</div>';
-?>
-
-<div align="center">
-    <h2><?php echo get_lang('TakeYourPhotos'); ?></h2>
-</div>
-<div align="center">
-<table><tr><td valign='top' align='center'>
-<h3 align='center'><?php echo get_lang('LocalInputImage'); ?></h3>
-    <!-- Including New Lib WebCamJS upgrading from JPEGCam -->
-    <script type="text/javascript" src="<?php echo api_get_path(WEB_PUBLIC_PATH); ?>assets/webcamjs/webcam.js"></script>
-    <!-- Adding a div container for the new live camera with some cool style options-->
-    <div class="webcamclip_bg">
-        <div id="chamilo-camera"></div>
-    </div>
-    <!-- Configure a few settings and attach the camera to the div container -->
-    <script>
-        Webcam.set({
-            width: 320,
-            height: 240,
-            image_format: 'jpeg',
-            jpeg_quality: 90
-        });
-        Webcam.attach( '#chamilo-camera' );
-        //This line Fix a minor bug that made a conflict with a videochat feature in another module file
-        $('video').addClass('skip');
-    </script>
-    <!-- Using now Jquery to do the webcamJS Functions and handle the server response (see webcam_receiver.php now in webcamJS directory) -->
-    <script>
-        $(document).ready(function() {
-           $('#btnCapture').click(function() {
-               Webcam.freeze();
-               return false;
-           });
-
-           $('#btnClean').click(function() {
-               Webcam.unfreeze();
-               return false;
-           });
-
-           $('#btnSave').click(function() {
-               snap();
-               return false;
-           });
-
-           $('#btnAuto').click(function() {
-               start_video();
-               return false;
-           });
-
-           $('#btnStop').click(function() {
-               stop_video();
-               return false;
-           });
-        });
-
-        function snap() {
-            Webcam.snap( function(data_uri) {
-                  var clip_filename='video_clip.jpg';
-                  var url = 'webcam_receiver.php?webcamname='+escape(clip_filename)+'&webcamdir=<?php echo $webcamdir; ?>&webcamuserid=<?php echo $webcamuserid; ?>';
-                  Webcam.upload(data_uri, url, function(code, response){
-                      $('#upload_results').html(
-                          '<h3>'+response+'</h3>' +
-                          '<div>'+
-                          '<img src="' + data_uri + '" class="webcamclip_bg">' +
-                          '</div>'+
-                          '</div>'+
-                          '<p hidden="true">'+code+'</p>'
-                      );
-                  });
-              });
-        }
-        var interval=null;
-        var timeout=null;
-        var counter=0;
-        var fps=1000;//one frame per second
-        var maxclip=25;//maximum number of clips
-        var maxtime=60000;//stop after one minute
-
-        function stop_video() {
-            interval=window.clearInterval(interval);
-            return false;
-        }
-
-        function start_video() {
-            interval=window.setInterval("clip_send_video()",fps);
-        }
+$actions = Display::toolbarAction(
+    'webcam_toolbar',
+    [
+        Display::url(
+            Display::return_icon(
+                'back.png',
+                get_lang('BackTo').' '.get_lang('DocumentsOverview'),
+                [],
+                ICON_SIZE_MEDIUM
+            ),
+            'document.php?id='.$documentId.'&'.api_get_cidreq()
+        ),
+    ]
+);
 
-        function clip_send_video() {
-           counter++;
-           timeout=setTimeout('stop_video()',maxtime);
-           if(maxclip>=counter){
-               snap();// clip and upload
-           }
-           else {
-               interval=window.clearInterval(interval);
-           }
-        }
-    </script>
-    </td><td width=50></td><td valign='top' align='center'>
-        <div id="upload_results" style="background-color:#ffffff;"></div>
-    </td></tr></table>
-    <!-- Implementing Button html5 Tags instead Inputs and some cool bootstrap button styles -->
-    <div>
-        <br/>
-            <form>
-                <br/>
-                <button id="btnCapture" class="btn btn-danger">
-                <em class="fa fa-camera"></em>
-                <?php echo get_lang('Snapshot'); ?>
-                </button>
-                <button id="btnClean" class="btn btn-success">
-                <em class="fa fa-refresh"></em>
-                <?php echo get_lang('Clean'); ?>
-                </button>
-                <button id="btnSave" class="btn btn-primary">
-                <em class="fa fa-save"></em>
-                <?php echo get_lang('Save'); ?>
-                </button>
-                &nbsp;&nbsp;||&nbsp;&nbsp;
-                <button id="btnAuto" class="btn btn-default">
-                <?php echo get_lang('Auto'); ?>
-                </button>
-                <button id="btnStop" class="btn btn-default">
-                <?php echo get_lang('Stop'); ?>
-                </button>
-                <br/>
-            </form>
-        <br/>
-   </div>
-</div>
+$template = new Template($nameTools);
+$template->assign('webcam_dir', $webcamdir);
+$template->assign('user_id', $userId);
+$template->assign('filename', 'video_clip.jpg');
 
-<?php
+$layout = $template->get_template('document/webcam.tpl');
+$content = $template->fetch($layout);
 
-Display :: display_footer();
+$template->assign('header', get_lang('TakeYourPhotos'));
+$template->assign('actions', $actions);
+$template->assign('content', $content);
+$template->display_one_col_template();

+ 3 - 3
main/exercise/TestCategory.php

@@ -158,7 +158,7 @@ class TestCategory
         $id = intval($this->id);
         $name = Database::escape_string($this->name);
         $description = Database::escape_string($this->description);
-        $cat = $this->getCategory($id);
+        $cat = $this->getCategory($id, $courseId);
         $courseId = empty($courseId) ? api_get_course_int_id() : (int) $courseId;
         $courseInfo = api_get_course_info_by_id($courseId);
         if (empty($courseInfo)) {
@@ -219,7 +219,6 @@ class TestCategory
         $courseId = empty($courseId) ? api_get_course_int_id() : (int) $courseId;
 
         $table = Database::get_course_table(TABLE_QUIZ_QUESTION_CATEGORY);
-        $field = Database::escape_string($field);
         $categories = [];
         if (empty($field)) {
             $sql = "SELECT id FROM $table
@@ -228,9 +227,10 @@ class TestCategory
             $res = Database::query($sql);
             while ($row = Database::fetch_array($res)) {
                 $category = new TestCategory();
-                $categories[] = $category->getCategory($row['id']);
+                $categories[] = $category->getCategory($row['id'], $courseId);
             }
         } else {
+            $field = Database::escape_string($field);
             $sql = "SELECT $field FROM $table
                     WHERE c_id = $courseId
                     ORDER BY $field ASC";

+ 1 - 0
main/exercise/answer.class.php

@@ -35,6 +35,7 @@ class Answer
     public $new_hotspot_coordinates;
     public $new_hotspot_type;
     public $autoId;
+    /** @var int Number of answers in the question */
     public $nbrAnswers;
     public $new_nbrAnswers;
     public $new_destination; // id of the next question if feedback option is set to Directfeedback

+ 86 - 92
main/exercise/exercise.class.php

@@ -83,6 +83,7 @@ class Exercise
     public $showPreviousButton;
     public $notifications;
     public $export = false;
+    public $autolaunch;
 
     /**
      * Constructor of the class.
@@ -192,6 +193,7 @@ class Exercise
             $this->globalCategoryId = isset($object->global_category_id) ? $object->global_category_id : null;
             $this->questionSelectionType = isset($object->question_selection_type) ? $object->question_selection_type : null;
             $this->hideQuestionTitle = isset($object->hide_question_title) ? (int) $object->hide_question_title : 0;
+            $this->autolaunch = isset($object->autolaunch) ? (int) $object->autolaunch : 0;
 
             $this->notifications = [];
             if (!empty($object->notifications)) {
@@ -869,7 +871,7 @@ class Exercise
                 $categoriesAddedInExercise = $cat->getCategoryExerciseTree(
                     $this,
                     $this->course['real_id'],
-                    'title DESC',
+                    'title ASC',
                     false,
                     true
                 );
@@ -3036,7 +3038,7 @@ class Exercise
             'orig_lp_item_id' => $safe_lp_item_id,
             'orig_lp_item_view_id' => $safe_lp_item_view_id,
             'exe_weighting' => $weight,
-            'user_ip' => api_get_real_ip(),
+            'user_ip' => Database::escape_string(api_get_real_ip()),
             'exe_date' => api_get_utc_datetime(),
             'exe_result' => 0,
             'steps_counter' => 0,
@@ -5483,7 +5485,6 @@ class Exercise
             if ($answerType == HOT_SPOT || $answerType == HOT_SPOT_ORDER) {
                 // We made an extra table for the answers
                 if ($show_result) {
-                    //	if ($origin != 'learnpath') {
                     echo '</table></td></tr>';
                     echo "
                         <tr>
@@ -5494,7 +5495,8 @@ class Exercise
                                     $(document).on('ready', function () {
                                         new HotspotQuestion({
                                             questionId: $questionId,
-                                            exerciseId: $exeId,
+                                            exerciseId: {$this->id},
+                                            exeId: $exeId,
                                             selector: '#hotspot-solution-$questionId',
                                             for: 'solution',
                                             relPath: '$relPath'
@@ -5504,7 +5506,6 @@ class Exercise
                             </td>
                         </tr>
                     ";
-                    //	}
                 }
             } elseif ($answerType == ANNOTATION) {
                 if ($show_result) {
@@ -5973,88 +5974,6 @@ class Exercise
         return $html;
     }
 
-    /**
-     * Create a quiz from quiz data.
-     *
-     * @param string  Title
-     * @param int     Time before it expires (in minutes)
-     * @param int     Type of exercise
-     * @param int     Whether it's randomly picked questions (1) or not (0)
-     * @param int     Whether the exercise is visible to the user (1) or not (0)
-     * @param int     Whether the results are show to the user (0) or not (1)
-     * @param int     Maximum number of attempts (0 if no limit)
-     * @param int     Feedback type
-     * @param int $propagateNegative
-     *
-     * @todo this was function was added due the import exercise via CSV
-     *
-     * @return int New exercise ID
-     */
-    public function createExercise(
-        $title,
-        $expired_time = 0,
-        $type = 2,
-        $random = 0,
-        $active = 1,
-        $results_disabled = 0,
-        $max_attempt = 0,
-        $feedback = 3,
-        $propagateNegative = 0
-    ) {
-        $tbl_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
-        $type = intval($type);
-        $random = intval($random);
-        $active = intval($active);
-        $results_disabled = intval($results_disabled);
-        $max_attempt = intval($max_attempt);
-        $feedback = intval($feedback);
-        $expired_time = intval($expired_time);
-        $title = Database::escape_string($title);
-        $propagateNegative = intval($propagateNegative);
-        $sessionId = api_get_session_id();
-        $course_id = api_get_course_int_id();
-        // Save a new quiz
-        $sql = "INSERT INTO $tbl_quiz (
-                c_id,
-                title,
-                type,
-                random,
-                active,
-                results_disabled,
-                max_attempt,
-                start_time,
-                end_time,
-                feedback_type,
-                expired_time,
-                session_id,
-                propagate_neg
-            )
-            VALUES (
-                '$course_id',
-                '$title',
-                $type,
-                $random,
-                $active,
-                $results_disabled,
-                $max_attempt,
-                '',
-                '',
-                $feedback,
-                $expired_time,
-                $sessionId,
-                $propagateNegative
-            )";
-        Database::query($sql);
-        $quiz_id = Database::insert_id();
-
-        if ($quiz_id) {
-            $sql = "UPDATE $tbl_quiz SET id = iid WHERE iid = {$quiz_id} ";
-            Database::query($sql);
-        }
-
-        return $quiz_id;
-    }
-
     /**
      * Returns the exercise result.
      *
@@ -6650,9 +6569,9 @@ class Exercise
      */
     public function get_stat_track_exercise_info_by_exe_id($exe_id)
     {
-        $track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
-        $exe_id = intval($exe_id);
-        $sql_track = "SELECT * FROM $track_exercises WHERE exe_id = $exe_id ";
+        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
+        $exe_id = (int) $exe_id;
+        $sql_track = "SELECT * FROM $table WHERE exe_id = $exe_id ";
         $result = Database::query($sql_track);
         $new_array = [];
         if (Database::num_rows($result) > 0) {
@@ -6669,6 +6588,51 @@ class Exercise
         return $new_array;
     }
 
+    /**
+     * @param int $exeId
+     *
+     * @return bool
+     */
+    public function removeAllQuestionToRemind($exeId)
+    {
+        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
+        $exeId = (int) $exeId;
+        if (empty($exeId)) {
+            return false;
+        }
+        $sql = "UPDATE $table 
+                SET questions_to_check = '' 
+                WHERE exe_id = $exeId ";
+        Database::query($sql);
+
+        return true;
+    }
+
+    /**
+     * @param int   $exeId
+     * @param array $questionList
+     *
+     * @return bool
+     */
+    public function addAllQuestionToRemind($exeId, $questionList = [])
+    {
+        $exeId = (int) $exeId;
+        if (empty($questionList)) {
+            return false;
+        }
+
+        $questionListToString = implode(',', $questionList);
+        $questionListToString = Database::escape_string($questionListToString);
+
+        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
+        $sql = "UPDATE $table 
+                SET questions_to_check = '$questionListToString' 
+                WHERE exe_id = $exeId";
+        Database::query($sql);
+
+        return true;
+    }
+
     /**
      * @param int    $exe_id
      * @param int    $question_id
@@ -6677,8 +6641,8 @@ class Exercise
     public function editQuestionToRemind($exe_id, $question_id, $action = 'add')
     {
         $exercise_info = self::get_stat_track_exercise_info_by_exe_id($exe_id);
-        $question_id = intval($question_id);
-        $exe_id = intval($exe_id);
+        $question_id = (int) $question_id;
+        $exe_id = (int) $exe_id;
         $track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
         if ($exercise_info) {
             if (empty($exercise_info['questions_to_check'])) {
@@ -7874,6 +7838,36 @@ class Exercise
         }
     }
 
+    /**
+     * @return int
+     */
+    public function getAutoLaunch()
+    {
+        return $this->autolaunch;
+    }
+
+    /**
+     * Clean auto launch settings for all exercise in course/course-session.
+     */
+    public function enableAutoLaunch()
+    {
+        $table = Database::get_course_table(TABLE_QUIZ_TEST);
+        $sql = "UPDATE $table SET autolaunch = 1
+                WHERE iid = ".$this->iId;
+        Database::query($sql);
+    }
+
+    /**
+     * Clean auto launch settings for all exercise in course/course-session.
+     */
+    public function cleanCourseLaunchSettings()
+    {
+        $table = Database::get_course_table(TABLE_QUIZ_TEST);
+        $sql = "UPDATE $table SET autolaunch = 0  
+                WHERE c_id = ".$this->course_id." AND session_id = ".$this->sessionId;
+        Database::query($sql);
+    }
+
     /**
      * Gets the question list ordered by the question_order setting (drag and drop).
      *

+ 43 - 4
main/exercise/exercise.php

@@ -79,11 +79,9 @@ Session::erase('duration_time');
 //General POST/GET/SESSION/COOKIES parameters recovery
 $origin = api_get_origin();
 $choice = isset($_REQUEST['choice']) ? Security::remove_XSS($_REQUEST['choice']) : null;
-
 $hpchoice = isset($_REQUEST['hpchoice']) ? Security::remove_XSS($_REQUEST['hpchoice']) : null;
 $exerciseId = isset($_REQUEST['exerciseId']) ? (int) $_REQUEST['exerciseId'] : null;
 $file = isset($_REQUEST['file']) ? Database::escape_string($_REQUEST['file']) : null;
-
 $learnpath_id = isset($_REQUEST['learnpath_id']) ? intval($_REQUEST['learnpath_id']) : null;
 $learnpath_item_id = isset($_REQUEST['learnpath_item_id']) ? intval($_REQUEST['learnpath_item_id']) : null;
 $page = isset($_REQUEST['page']) ? intval($_REQUEST['page']) : null;
@@ -99,6 +97,13 @@ if (api_is_in_gradebook()) {
     ];
 }
 
+$autoLaunchAvailable = false;
+if (api_get_course_setting('enable_exercise_auto_launch') == 1 &&
+    api_get_configuration_value('allow_exercise_auto_launch')
+) {
+    $autoLaunchAvailable = true;
+}
+
 $nameTools = get_lang('Exercises');
 $errorXmlExport = null;
 if ($is_allowedToEdit && !empty($choice) && $choice == 'exportqti2') {
@@ -130,7 +135,7 @@ if ($is_allowedToEdit && !empty($choice) && $choice == 'exportqti2') {
         rmdir($temp_zip_dir);
         exit; //otherwise following clicks may become buggy
     } else {
-        $errorXmlExport = Display :: return_message(get_lang('ErrorWritingXMLFile'), 'error');
+        $errorXmlExport = Display::return_message(get_lang('ErrorWritingXMLFile'), 'error');
     }
 }
 
@@ -205,6 +210,14 @@ if ($is_allowedToEdit) {
         if ($objExerciseTmp->read($exerciseId)) {
             if ($check) {
                 switch ($choice) {
+                    case 'enable_launch':
+                        $objExerciseTmp->cleanCourseLaunchSettings();
+                        $objExerciseTmp->enableAutoLaunch();
+                        Display::addFlash(Display::return_message(get_lang('Updated')));
+                        break;
+                    case 'disable_launch':
+                        $objExerciseTmp->cleanCourseLaunchSettings();
+                        break;
                     case 'delete':
                         // deletes an exercise
                         if ($exercise_action_locked == false) {
@@ -737,7 +750,7 @@ if (!empty($exerciseList)) {
                 $sql = "SELECT count(*) count FROM $TBL_EXERCISE_QUESTION
                         WHERE c_id = $courseId AND exercice_id = $my_exercise_id";
                 $sqlresult = Database::query($sql);
-                $rowi = Database :: result($sqlresult, 0, 0);
+                $rowi = intval(Database :: result($sqlresult, 0, 0));
 
                 if ($session_id == $row['session_id']) {
                     // Questions list
@@ -756,6 +769,32 @@ if (!empty($exerciseList)) {
                     $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>';
 
+                    // Auto launch
+                    if ($autoLaunchAvailable) {
+                        $autoLaunch = $exercise->getAutoLaunch();
+                        if (empty($autoLaunch)) {
+                            $actions .= Display::url(
+                                Display::return_icon(
+                                    'launch_na.png',
+                                    get_lang('Enable'),
+                                    '',
+                                    ICON_SIZE_SMALL
+                                ),
+                                'exercise.php?'.api_get_cidreq().'&choice=enable_launch&sec_token='.$token.'&page='.$page.'&exerciseId='.$row['id']
+                            );
+                        } else {
+                            $actions .= Display::url(
+                                Display::return_icon(
+                                    'launch.png',
+                                    get_lang('Disable'),
+                                    '',
+                                    ICON_SIZE_SMALL
+                                ),
+                                'exercise.php?'.api_get_cidreq().'&choice=disable_launch&sec_token='.$token.'&page='.$page.'&exerciseId='.$row['id']
+                            );
+                        }
+                    }
+
                     // Export
                     $actions .= Display::url(
                         Display::return_icon('cd.png', get_lang('CopyExercise')),

+ 39 - 7
main/exercise/exercise_reminder.php

@@ -140,26 +140,46 @@ echo '<script>
         //Normal inputs
         window.location = "exercise_result.php?'.api_get_cidreq().'&exe_id='.$exe_id.'&" + lp_data;
     }
+    
+    function changeOptionStatus(status) 
+    {    
+        $("input[type=checkbox]").each(function () {                 
+            $(this).prop("checked", status);                                                
+        });    
+        
+        var action = ""; 
+        var extraOption = "remove_all";       
+        if (status == 1) {
+            extraOption = "add_all";
+        }     
+        $.ajax({
+            url: "'.api_get_path(WEB_AJAX_PATH).'exercise.ajax.php?'.api_get_cidreq().'&a=add_question_to_reminder",
+            data: "option="+extraOption+"&exe_id='.$exe_id.'&action="+action,
+            success: function(returnValue) {
+            }
+        });  
+    }
 
     function review_questions() {
-        var is_checked = 1;
+        var isChecked = 1;
         $("input[type=checkbox]").each(function () {
-            if ($(this).attr("checked") == "checked") {
-                is_checked = 2;
+            if ($(this).prop("checked")) {
+                isChecked = 2;
                 return false;
             }
         });
 
-        if (is_checked == 1) {
+        if (isChecked == 1) {
             $("#message").addClass("warning-message");
             $("#message").html("'.addslashes(get_lang('SelectAQuestionToReview')).'");
+        } else {
+            window.location = "exercise_submit.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id.'&reminder=2&" + lp_data;
         }
-        window.location = "exercise_submit.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id.'&reminder=2&" + lp_data;
     }
 
     function save_remind_item(obj, question_id) {
         var action = "";
-        if ($(obj).is(\':checked\')) {
+        if ($(obj).prop("checked")) {
             action = "add";
         } else {
             action = "delete";
@@ -167,7 +187,7 @@ echo '<script>
         $.ajax({
             url: "'.api_get_path(WEB_AJAX_PATH).'exercise.ajax.php?'.api_get_cidreq().'&a=add_question_to_reminder",
             data: "question_id="+question_id+"&exe_id='.$exe_id.'&action="+action,
-            success: function(return_value) {
+            success: function(returnValue) {
             }
         });
     }
@@ -247,6 +267,18 @@ $exerciseActions = Display::url(
     ['onclick' => 'review_questions();', 'class' => 'btn btn-success']
 );
 
+$exerciseActions .= '&nbsp;'.Display::url(
+    get_lang('SelectAll'),
+    'javascript://',
+    ['onclick' => 'changeOptionStatus(1);', 'class' => 'btn btn-default']
+);
+
+$exerciseActions .= '&nbsp;'.Display::url(
+    get_lang('UnSelectAll'),
+    'javascript://',
+    ['onclick' => 'changeOptionStatus(0);', 'class' => 'btn btn-default']
+);
+
 $exerciseActions .= '&nbsp;'.Display::url(
     get_lang('EndTest'),
     'javascript://',

+ 26 - 3
main/exercise/exercise_report.php

@@ -53,14 +53,30 @@ $TBL_LP_ITEM_VIEW = Database::get_course_table(TABLE_LP_ITEM_VIEW);
 $allowCoachFeedbackExercises = api_get_setting('allow_coach_feedback_exercises') === 'true';
 
 $course_id = api_get_course_int_id();
-$exercise_id = isset($_REQUEST['exerciseId']) ? intval($_REQUEST['exerciseId']) : null;
+$exercise_id = isset($_REQUEST['exerciseId']) ? (int) $_REQUEST['exerciseId'] : 0;
 $locked = api_resource_is_locked_by_gradebook($exercise_id, LINK_EXERCISE);
+$sessionId = api_get_session_id();
 
 if (empty($exercise_id)) {
     api_not_allowed(true);
 }
 
-if (!$is_allowedToEdit && !$allowCoachFeedbackExercises) {
+$blockPage = true;
+if (empty($sessionId)) {
+    if ($is_allowedToEdit) {
+        $blockPage = false;
+    }
+} else {
+    if ($allowCoachFeedbackExercises && api_is_coach($sessionId, $course_id)) {
+        $blockPage = false;
+    } else {
+        if ($is_allowedToEdit) {
+            $blockPage = false;
+        }
+    }
+}
+
+if ($blockPage) {
     api_not_allowed(true);
 }
 
@@ -587,7 +603,14 @@ if ($is_allowedToEdit || $is_tutor) {
     $column_model = [
         ['name' => 'firstname', 'index' => 'firstname', 'width' => '50', 'align' => 'left', 'search' => 'true'],
         ['name' => 'lastname', 'index' => 'lastname', 'width' => '50', 'align' => 'left', 'formatter' => 'action_formatter', 'search' => 'true'],
-        ['name' => 'login', 'index' => 'username', 'width' => '40', 'align' => 'left', 'search' => 'true', 'hidden' => 'true'],
+        [
+            'name' => 'login',
+            'index' => 'username',
+            'width' => '40',
+            'align' => 'left',
+            'search' => 'true',
+            'hidden' => api_get_configuration_value('exercise_attempts_report_show_username') ? 'false' : 'true',
+        ],
         ['name' => 'group_name', 'index' => 'group_id', 'width' => '40', 'align' => 'left', 'search' => 'true', 'stype' => 'select',
             //for the bottom bar
             'searchoptions' => [

+ 18 - 2
main/exercise/exercise_result.class.php

@@ -80,7 +80,8 @@ class ExerciseResult
                     exe_user_id as excruid,
                     te.exe_duration as duration,
                     te.orig_lp_id as orig_lp_id,
-                    tlm.name as lp_name
+                    tlm.name as lp_name,
+                    user.username
                 FROM $TBL_EXERCISES  AS ce
                 INNER JOIN $TBL_TRACK_EXERCISES AS te 
                 ON (te.exe_exo_id = ce.id)
@@ -110,7 +111,8 @@ class ExerciseResult
                         te.exe_duration as duration,
                         ce.results_disabled as exdisabled,
                         te.orig_lp_id as orig_lp_id,
-                        tlm.name as lp_name
+                        tlm.name as lp_name,
+                        user.username
                     FROM $TBL_EXERCISES  AS ce
                     INNER JOIN $TBL_TRACK_EXERCISES AS te 
                     ON (te.exe_exo_id = ce.id)
@@ -212,6 +214,7 @@ class ExerciseResult
                     }
                     $return[$i]['user_id'] = $results[$i]['excruid'];
                     $return[$i]['email'] = $results[$i]['exemail'];
+                    $return[$i]['username'] = $results[$i]['username'];
                 }
                 $return[$i]['title'] = $result['extitle'];
                 $return[$i]['start_date'] = api_get_local_time($result['exstart']);
@@ -256,6 +259,7 @@ class ExerciseResult
 
                             $return[$i]['user_id'] = $student['user_id'];
                             $return[$i]['email'] = $student['email'];
+                            $return[$i]['username'] = $student[$i]['username'];
                         }
                         $return[$i]['title'] = null;
                         $return[$i]['start_date'] = null;
@@ -332,6 +336,7 @@ class ExerciseResult
             $data .= get_lang('OfficialCode').';';
         }
 
+        $data .= get_lang('LoginName').';';
         $data .= get_lang('Email').';';
         $data .= get_lang('Groups').';';
 
@@ -379,6 +384,7 @@ class ExerciseResult
             }
 
             // Email
+            $data .= str_replace("\r\n", '  ', api_html_entity_decode(strip_tags($row['username']), ENT_QUOTES, $charset)).';';
             $data .= str_replace("\r\n", '  ', api_html_entity_decode(strip_tags($row['email']), ENT_QUOTES, $charset)).';';
             $data .= str_replace("\r\n", '  ', implode(", ", GroupManager::get_user_group_name($row['user_id']))).';';
 
@@ -503,6 +509,7 @@ class ExerciseResult
                 $column++;
             }
 
+            $worksheet->setCellValueByColumnAndRow($column++, $line, get_lang('LoginName'));
             $worksheet->setCellValueByColumnAndRow($column, $line, get_lang('Email'));
             $column++;
         }
@@ -614,6 +621,15 @@ class ExerciseResult
                     $column++;
                 }
 
+                $worksheet->setCellValueByColumnAndRow(
+                    $column++,
+                    $line,
+                    api_html_entity_decode(
+                        strip_tags($row['username']),
+                        ENT_QUOTES,
+                        $charset
+                    )
+                );
                 $worksheet->setCellValueByColumnAndRow(
                     $column,
                     $line,

+ 1 - 1
main/exercise/exercise_result.php

@@ -40,7 +40,7 @@ if (empty($remind_list)) {
     $remind_list = isset($_REQUEST['remind_list']) ? $_REQUEST['remind_list'] : null;
 }
 
-$exe_id = isset($_REQUEST['exe_id']) ? intval($_REQUEST['exe_id']) : 0;
+$exe_id = isset($_REQUEST['exe_id']) ? (int) $_REQUEST['exe_id'] : 0;
 
 if (empty($objExercise)) {
     // Redirect to the exercise overview

+ 5 - 5
main/exercise/exercise_show.php

@@ -82,7 +82,6 @@ if (empty($objExercise)) {
     $objExercise = Session::read('objExercise');
 }
 
-$exeId = isset($_REQUEST['id']) ? $_REQUEST['id'] : null;
 $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : null;
 
 $courseInfo = api_get_course_info();
@@ -92,7 +91,6 @@ $is_allowedToEdit = api_is_allowed_to_edit(null, true) ||
     api_is_session_admin() ||
     api_is_drh() ||
     api_is_student_boss();
-
 if (!empty($sessionId) && !$is_allowedToEdit) {
     if (api_is_course_session_coach(
         $currentUserId,
@@ -462,7 +460,8 @@ foreach ($questionList as $questionId) {
                                     $(document).on('ready', function () {
                                         new HotspotQuestion({
                                             questionId: $questionId,
-                                            exerciseId: $id,
+                                            exerciseId: {$objExercise->id},                                            
+                                            exeId: $id,
                                             selector: '#hotspot-solution-$questionId-$id',
                                             for: 'solution',
                                             relPath: '$relPath'
@@ -630,7 +629,8 @@ foreach ($questionList as $questionId) {
                                     $(document).on('ready', function () {
                                         new HotspotQuestion({
                                             questionId: $questionId,
-                                            exerciseId: $id,
+                                            exerciseId: {$objExercise->id},
+                                            exeId: $id,
                                             selector: '#hotspot-solution',
                                             for: 'solution',
                                             relPath: '$relPath'
@@ -1110,7 +1110,7 @@ if ($origin != 'learnpath') {
             'action' => 'view',
             'lp_id' => $learnpath_id,
             'lp_item_id' => $learnpath_item_id,
-            'exeId' => $exeId,
+            'exeId' => $id,
             'fb_type' => $feedback_type,
         ]);
         $href = ($lp_mode == 'fullscreen')

+ 5 - 5
main/exercise/fill_blanks.class.php

@@ -1266,8 +1266,8 @@ class FillBlanks extends Question
         switch ($type) {
             case self::FILL_THE_BLANK_MENU:
                 $listPossibleAnswers = self::getFillTheBlankMenuAnswers($correct, false);
-                $correctAnswerHtml .= "<span style='color: green'>".$listPossibleAnswers[0]."</span>";
-                $correctAnswerHtml .= " <span style='font-weight:normal'>(";
+                $correctAnswerHtml .= "<span class='correct-answer'><strong>".$listPossibleAnswers[0]."</strong>";
+                $correctAnswerHtml .= " (";
                 for ($i = 1; $i < count($listPossibleAnswers); $i++) {
                     $correctAnswerHtml .= $listPossibleAnswers[$i];
                     if ($i != count($listPossibleAnswers) - 1) {
@@ -1282,11 +1282,11 @@ class FillBlanks extends Question
                 if (count($listCorrects) > 0) {
                     $firstCorrect = $listCorrects[0];
                 }
-                $correctAnswerHtml = "<span class='feedback-green'>".$firstCorrect."</span>";
+                $correctAnswerHtml = "<span class='correct-answer'>".$firstCorrect."</span>";
                 break;
             case self::FILL_THE_BLANK_STANDARD:
             default:
-                $correctAnswerHtml = "<span class='feedback-green'>".$correct."</span>";
+                $correctAnswerHtml = "<span class='correct-answer'>".$correct."</span>";
         }
 
         if ($hideExpectedAnswer) {
@@ -1297,7 +1297,7 @@ class FillBlanks extends Question
 
         $result = "<span class='feedback-question'>";
         $result .= $iconAnswer."<span class='$style'>".$answer."</span>";
-        $result .= "<span class='feedback-separator'> / </span>";
+        $result .= "<span class='feedback-separator'>|</span>";
         $result .= $correctAnswerHtml;
         $result .= "</span>";
 

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

@@ -58,7 +58,7 @@ class GlobalMultipleAnswer extends Question
         if (!empty($this->id)) {
             $answer = new Answer($this->id);
             $answer->read();
-            if (count($answer->nbrAnswers) > 0 && !$form->isSubmitted()) {
+            if ($answer->nbrAnswers > 0 && !$form->isSubmitted()) {
                 $nb_answers = $answer->nbrAnswers;
             }
         }

+ 28 - 10
main/exercise/hotpotatoes.php

@@ -185,6 +185,7 @@ if ((api_is_allowed_to_edit(null, true)) && (($finish == 0) || ($finish == 2)))
                     }
                 }
 
+                // Set the HotPotatoes title in the document record's "comment" field
                 $title = @htmlspecialchars(
                     GetQuizName(
                         $filename,
@@ -193,17 +194,34 @@ if ((api_is_allowed_to_edit(null, true)) && (($finish == 0) || ($finish == 2)))
                     ENT_COMPAT,
                     api_get_system_encoding()
                 );
-                $query = "UPDATE $dbTable
+                $sessionAnd = ' AND session_id IS NULL ';
+                if ($sessionId) {
+                    $sessionAnd = " AND session_id = $sessionId ";
+                }
+                $path = $uploadPath.'/'.$fld.'/'.$filename;
+                // Find the proper record
+                $select = "SELECT iid FROM $dbTable
+                          WHERE c_id = $course_id
+                          $sessionAnd
+                          AND path = '$path'";
+                $query = Database::query($select);
+                if (Database::num_rows($query)) {
+                    $row = Database::fetch_array($query);
+                    $hotPotatoesDocumentId = $row['iid'];
+                    // Update the record with the 'comment' (HP title)
+                    $query = "UPDATE $dbTable
                           SET comment='".Database::escape_string($title)."'
-                          WHERE c_id = $course_id AND path=\"".$uploadPath."/".$fld."/".$filename."\"";
-                Database::query($query);
-                api_item_property_update(
-                    $_course,
-                    TOOL_QUIZ,
-                    $id,
-                    'QuizAdded',
-                    api_get_user_id()
-                );
+                          WHERE iid = $hotPotatoesDocumentId";
+                    Database::query($query);
+                    // Mark the addition of the HP quiz in the item_property table
+                    api_item_property_update(
+                        $_course,
+                        TOOL_QUIZ,
+                        $hotPotatoesDocumentId,
+                        'QuizAdded',
+                        api_get_user_id()
+                    );
+                }
             } else {
                 $finish = 0;
             }

+ 45 - 22
main/exercise/hotspot_answers.as.php

@@ -5,8 +5,7 @@ use Chamilo\CoreBundle\Entity\TrackEHotspot;
 use Chamilo\CourseBundle\Entity\CQuizAnswer;
 
 /**
- * This file generates the ActionScript variables code used by the
- * HotSpot .swf.
+ * This file generates a json answer to the question preview.
  *
  * @package chamilo.exercise
  *
@@ -14,14 +13,44 @@ use Chamilo\CourseBundle\Entity\CQuizAnswer;
  */
 require_once __DIR__.'/../inc/global.inc.php';
 
-// Set vars
-$questionId = intval($_GET['modifyAnswers']);
-$exe_id = intval($_GET['exe_id']);
+api_protect_course_script();
+
+$questionId = isset($_GET['modifyAnswers']) ? (int) $_GET['modifyAnswers'] : 0;
+$exerciseId = isset($_GET['exerciseId']) ? (int) $_GET['exerciseId'] : 0;
+$exeId = isset($_GET['exeId']) ? (int) $_GET['exeId'] : 0;
+$userId = api_get_user_id();
+$courseId = api_get_course_int_id();
+$objExercise = new Exercise($courseId);
+
+// Check if student has access to the hotspot answers
+if (!api_is_allowed_to_edit(null, true)) {
+    if (empty($exeId)) {
+        api_not_allowed();
+    }
+    $trackExerciseInfo = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId);
+
+    if (empty($trackExerciseInfo)) {
+        api_not_allowed();
+    }
+
+    // Different exercise
+    if ($exerciseId != $trackExerciseInfo['exe_exo_id']) {
+        api_not_allowed();
+    }
+
+    // Different user
+    if ($trackExerciseInfo['exe_user_id'] != $userId) {
+        api_not_allowed();
+    }
+}
+
+$objQuestion = Question::read($questionId, $courseId);
+$objExercise->read($exerciseId);
+
+if (empty($objQuestion) || empty($objExercise)) {
+    exit;
+}
 
-$objQuestion = Question::read($questionId);
-$trackExerciseInfo = ExerciseLib::get_exercise_track_exercise_info($exe_id);
-$objExercise = new Exercise(api_get_course_int_id());
-$objExercise->read($trackExerciseInfo['exe_exo_id']);
 $em = Database::getManager();
 $documentPath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document';
 $picturePath = $documentPath.'/images';
@@ -29,7 +58,6 @@ $pictureName = $objQuestion->getPictureFilename();
 $pictureSize = getimagesize($picturePath.'/'.$pictureName);
 $pictureWidth = $pictureSize[0];
 $pictureHeight = $pictureSize[1];
-$course_id = api_get_course_int_id();
 
 $data = [];
 $data['type'] = 'solution';
@@ -59,7 +87,6 @@ $data['courseCode'] = $_course['path'];
 $data['hotspots'] = [];
 
 $showTotalScoreAndUserChoicesInLastAttempt = true;
-
 if ($objExercise->selectResultsDisabled() == RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT) {
     $showOnlyScore = true;
     $showResults = true;
@@ -67,14 +94,13 @@ if ($objExercise->selectResultsDisabled() == RESULT_DISABLE_SHOW_SCORE_ATTEMPT_S
         $attempts = Event::getExerciseResultsByUser(
             api_get_user_id(),
             $objExercise->id,
-            api_get_course_int_id(),
+            $courseId,
             api_get_session_id(),
             $trackExerciseInfo['orig_lp_id'],
             $trackExerciseInfo['orig_lp_item_id'],
             'desc'
         );
         $numberAttempts = count($attempts);
-
         $showTotalScoreAndUserChoicesInLastAttempt = false;
 
         if ($numberAttempts >= $objExercise->attempts) {
@@ -86,7 +112,6 @@ if ($objExercise->selectResultsDisabled() == RESULT_DISABLE_SHOW_SCORE_ATTEMPT_S
 }
 
 $hideExpectedAnswer = false;
-
 if ($objExercise->selectFeedbackType() == 0 && $objExercise->selectResultsDisabled() == 2) {
     $hideExpectedAnswer = true;
 }
@@ -103,14 +128,14 @@ if (!$hideExpectedAnswer) {
 
     if ($objQuestion->selectType() == HOT_SPOT_DELINEATION) {
         $qb
-            ->where($qb->expr()->eq('a.cId', $course_id))
-            ->andWhere($qb->expr()->eq('a.questionId', intval($questionId)))
+            ->where($qb->expr()->eq('a.cId', $courseId))
+            ->andWhere($qb->expr()->eq('a.questionId', $questionId))
             ->andWhere($qb->expr()->neq('a.hotspotType', 'noerror'))
             ->orderBy('a.id', 'ASC');
     } else {
         $qb
-            ->where($qb->expr()->eq('a.cId', $course_id))
-            ->andWhere($qb->expr()->eq('a.questionId', intval($questionId)))
+            ->where($qb->expr()->eq('a.cId', $courseId))
+            ->andWhere($qb->expr()->eq('a.questionId', $questionId))
             ->orderBy('a.position', 'ASC');
     }
 
@@ -139,7 +164,6 @@ if (!$hideExpectedAnswer) {
                 $hotSpot['type'] = 'delineation';
                 break;
         }
-
         $hotSpot['coord'] = $hotSpotAnswer->getHotspotCoordinates();
         $data['hotspots'][] = $hotSpot;
     }
@@ -152,8 +176,8 @@ $rs = $em
     ->findBy(
         [
             'hotspotQuestionId' => $questionId,
-            'cId' => $course_id,
-            'hotspotExeId' => $exe_id,
+            'cId' => $courseId,
+            'hotspotExeId' => $exeId,
         ],
         ['hotspotAnswerId' => 'ASC']
     );
@@ -164,7 +188,6 @@ foreach ($rs as $row) {
 }
 
 $data['done'] = 'done';
-
 header('Content-Type: application/json');
 
 echo json_encode($data);

+ 26 - 13
main/exercise/question.class.php

@@ -649,28 +649,35 @@ abstract class Question
     /**
      * Get default hot spot folder in documents.
      *
+     * @param array $courseInfo
+     *
      * @return string
      */
-    public function getHotSpotFolderInCourse()
+    public function getHotSpotFolderInCourse($courseInfo = [])
     {
-        if (empty($this->course) || empty($this->course['directory'])) {
+        $courseInfo = empty($courseInfo) ? $this->course : $courseInfo;
+
+        if (empty($courseInfo) || empty($courseInfo['directory'])) {
             // Stop everything if course is not set.
             api_not_allowed();
         }
 
-        $pictureAbsolutePath = api_get_path(SYS_COURSE_PATH).$this->course['directory'].'/document/images/';
+        $pictureAbsolutePath = api_get_path(SYS_COURSE_PATH).$courseInfo['directory'].'/document/images/';
         $picturePath = basename($pictureAbsolutePath);
 
         if (!is_dir($picturePath)) {
             create_unexisting_directory(
-                $this->course,
+                $courseInfo,
                 api_get_user_id(),
                 0,
                 0,
                 0,
                 dirname($pictureAbsolutePath),
                 '/'.$picturePath,
-                $picturePath
+                $picturePath,
+                '',
+                false,
+                false
             );
         }
 
@@ -772,7 +779,7 @@ abstract class Question
      * @author Olivier Brouckaert
      *
      * @param int   $questionId - ID of the target question
-     * @param array $courseInfo
+     * @param array $courseInfo destination course info
      *
      * @return bool - true if copied, otherwise false
      */
@@ -783,8 +790,12 @@ abstract class Question
         }
 
         $course_id = $courseInfo['real_id'];
-        $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
-        $destination_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/images';
+        $destination_path = $this->getHotSpotFolderInCourse($courseInfo);
+
+        if (empty($destination_path)) {
+            return false;
+        }
+
         $source_path = $this->getHotSpotFolderInCourse();
 
         // if the question has got an ID and if the picture exists
@@ -792,12 +803,13 @@ abstract class Question
             return false;
         }
 
+        $sourcePictureName = $this->getPictureFilename($course_id);
         $picture = $this->generatePictureName();
-
-        if (file_exists($source_path.'/'.$this->picture)) {
+        $result = false;
+        if (file_exists($source_path.'/'.$sourcePictureName)) {
             // for backward compatibility
-            $result = @copy(
-                $source_path.'/'.$this->picture,
+            $result = copy(
+                $source_path.'/'.$sourcePictureName,
                 $destination_path.'/'.$picture
             );
         } else {
@@ -818,7 +830,8 @@ abstract class Question
             return false;
         }
 
-        $sql = "UPDATE $TBL_QUESTIONS SET
+        $table = Database::get_course_table(TABLE_QUIZ_QUESTION);
+        $sql = "UPDATE $table SET
                 picture = '".Database::escape_string($picture)."'
                 WHERE c_id = $course_id AND id='".intval($questionId)."'";
         Database::query($sql);

+ 6 - 0
main/exercise/question_create.php

@@ -14,6 +14,12 @@ $this_section = SECTION_COURSES;
 // notice for unauthorized people.
 api_protect_course_script(true);
 
+$allow = api_is_allowed_to_edit();
+
+if (!$allow) {
+    api_not_allowed(true);
+}
+
 // breadcrumbs
 $interbreadcrumb[] = ["url" => "exercise.php", "name" => get_lang('Exercises')];
 

+ 16 - 17
main/exercise/upload_exercise.php

@@ -266,17 +266,16 @@ function lp_upload_quiz_action_handling()
 
         // Quiz object
         $exercise = new Exercise();
-        $quiz_id = $exercise->createExercise(
-            $quizTitle,
-            $expired_time,
-            $type,
-            $random,
-            $active,
-            $results,
-            $max_attempt,
-            $feedback,
-            $propagateNegative
-        );
+        $exercise->updateTitle($quizTitle);
+        $exercise->updateExpiredTime($expired_time);
+        $exercise->updateType($type);
+        $exercise->setRandom($random);
+        $exercise->active = $active;
+        $exercise->updateResultsDisabled($results);
+        $exercise->updateAttempts($max_attempt);
+        $exercise->updateFeedbackType($feedback);
+        $exercise->updatePropagateNegative($propagateNegative);
+        $quiz_id = $exercise->save();
 
         if ($quiz_id) {
             // insert into the item_property table
@@ -387,7 +386,7 @@ function lp_upload_quiz_action_handling()
                                 $score = 0;
                                 if (strtolower($answer_data['extra']) == 'x') {
                                     $correct = 1;
-                                    $score = isset($scoreList[$i]) ? $scoreList[$i] : null;
+                                    $score = isset($scoreList[$i]) ? $scoreList[$i] : 0;
                                     $comment = isset($feedbackTrueList[$i]) ? $feedbackTrueList[$i] : '';
                                 } else {
                                     $comment = isset($feedbackFalseList[$i]) ? $feedbackFalseList[$i] : '';
@@ -439,7 +438,7 @@ function lp_upload_quiz_action_handling()
                                     $id
                                 );
 
-                                $total += $score;
+                                $total += (float) $score;
                                 $id++;
                             }
 
@@ -461,7 +460,7 @@ function lp_upload_quiz_action_handling()
                                         $questionObj->updateWeighting($total);
                                         break;
                                 }
-                                $questionObj->save();
+                                $questionObj->save($exercise);
                             }
                         }
                         break;
@@ -470,7 +469,7 @@ function lp_upload_quiz_action_handling()
                         $questionObj = Question::read($question_id, $courseId);
                         if ($questionObj) {
                             $questionObj->updateWeighting($globalScore);
-                            $questionObj->save();
+                            $questionObj->save($exercise);
                         }
                         break;
                     case FILL_IN_BLANKS:
@@ -503,7 +502,7 @@ function lp_upload_quiz_action_handling()
                         $questionObj = Question::read($question_id, $courseId);
                         if ($questionObj) {
                             $questionObj->updateWeighting($globalScore);
-                            $questionObj->save();
+                            $questionObj->save($exercise);
                         }
                         break;
                     case MATCHING:
@@ -534,7 +533,7 @@ function lp_upload_quiz_action_handling()
                         $questionObj = Question::read($question_id, $courseId);
                         if ($questionObj) {
                             $questionObj->updateWeighting($globalScore);
-                            $questionObj->save();
+                            $questionObj->save($exercise);
                         }
                         break;
                 }

+ 1 - 0
main/forum/forumfunction.inc.php

@@ -1479,6 +1479,7 @@ function get_forum_categories($id = '', $courseId = 0, $sessionId = 0)
                     $condition_session
                 ORDER BY forum_categories.cat_order ASC";
     }
+
     $result = Database::query($sql);
     $forum_categories_list = [];
     while ($row = Database::fetch_assoc($result)) {

+ 8 - 2
main/gradebook/certificate_report.php

@@ -14,10 +14,16 @@ $cidReset = true;
 
 require_once __DIR__.'/../inc/global.inc.php';
 
-$this_section = SECTION_TRACKING;
-
 api_block_anonymous_users();
 
+$is_allowedToTrack = api_is_platform_admin(true) || api_is_student_boss();
+
+if (!$is_allowedToTrack) {
+    api_not_allowed(true);
+}
+
+$this_section = SECTION_TRACKING;
+
 $interbreadcrumb[] = [
     "url" => api_is_student_boss() ? "#" : api_get_path(WEB_CODE_PATH)."mySpace/index.php?".api_get_cidreq(),
     "name" => get_lang("MySpace"),

+ 31 - 45
main/gradebook/index.php

@@ -667,17 +667,6 @@ if (isset($_GET['studentoverview'])) {
     // then Category::load() will create a new 'root' category with empty
     // course and session fields in memory (Category::create_root_category())
     if ($_in_course === true) {
-        // When *inside* a course, we want to make sure there is one (and only
-        // one) category for this course or for this session.
-
-        //hack for delete a gradebook from inside course
-        /*
-        $clean_deletecat = isset($_GET['deletecat']) ? intval($_GET['deletecat']) : null;
-        if (!empty($clean_deletecat)) {
-            exit;
-        }
-        //end hack*/
-
         $cats = Category:: load(
             null,
             null,
@@ -779,46 +768,44 @@ if (!empty($selectCat)) {
 
 if (!api_is_allowed_to_edit(null, true)) {
     $actionsLeft .= Display::url(
-        Display::returnFontAwesomeIcon('file-pdf-o').get_lang(
-            'DownloadReportPdf'
-        ),
-        api_get_self()."?".api_get_self()."&action=export_table",
+        Display::returnFontAwesomeIcon('file-pdf-o').get_lang('DownloadReportPdf'),
+        api_get_self().'?action=export_table',
         ['class' => 'btn btn-default']
     );
 }
 
-if (!empty($actionsLeft)) {
-    echo $toolbar = Display::toolbarAction(
-        'gradebook-student-actions',
-        [$actionsLeft]
-    );
-}
-
-if (api_is_allowed_to_edit(null, true)) {
-    // Tool introduction
-    Display::display_introduction_section(
-        TOOL_GRADEBOOK,
-        ['ToolbarSet' => 'AssessmentsIntroduction']
-    );
+if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)) {
+    echo '<meta http-equiv="refresh" content="0;url='.api_get_self().'?'.api_get_cidreq().'" />';
+} else {
+    if (!empty($actionsLeft)) {
+        echo $toolbar = Display::toolbarAction(
+            'gradebook-student-actions',
+            [$actionsLeft]
+        );
+    }
 
-    if (((empty($selectCat)) || (isset($_GET['cidReq']) && $_GET['cidReq'] !== '')) ||
-        (isset($_GET['isStudentView']) && $_GET['isStudentView'] == 'false')
-    ) {
-        $cats = Category:: load(
-            null,
-            null,
-            $course_code,
-            null,
-            null,
-            $session_id,
-            false
+    if (api_is_allowed_to_edit(null, true)) {
+        // Tool introduction
+        Display::display_introduction_section(
+            TOOL_GRADEBOOK,
+            ['ToolbarSet' => 'AssessmentsIntroduction']
         );
+
+        if (((empty($selectCat)) || (isset($_GET['cidReq']) && $_GET['cidReq'] !== '')) ||
+            (isset($_GET['isStudentView']) && $_GET['isStudentView'] == 'false')
+        ) {
+            $cats = Category:: load(
+                null,
+                null,
+                $course_code,
+                null,
+                null,
+                $session_id,
+                false
+            );
+        }
     }
-}
 
-if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)) {
-    echo '<meta http-equiv="refresh" content="0;url='.api_get_self().'?'.api_get_cidreq().'" />';
-} else {
     $cats = Category::load(
         null,
         null,
@@ -894,6 +881,7 @@ if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)
 
         $i = 0;
         $allcat = [];
+        $model = ExerciseLib::getCourseScoreModel();
 
         /** @var Category $cat */
         foreach ($cats as $cat) {
@@ -954,8 +942,6 @@ if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)
                     $loadStats
                 );
 
-                $model = ExerciseLib::getCourseScoreModel();
-
                 if (api_is_allowed_to_edit()) {
                     $gradebookTable->td_attributes = [
                         4 => 'class="text-center"',

+ 1 - 1
main/gradebook/lib/GradebookUtils.php

@@ -801,7 +801,7 @@ class GradebookUtils
             $userListCondition = implode("','", $userList);
             $sql .= " AND u.user_id IN ('$userListCondition')";
         }
-        $sql .= ' ORDER BY u.firstname';
+        $sql .= ' ORDER BY '.(api_sort_by_first_name() ? 'u.firstname' : 'u.lastname');
         $rs = Database::query($sql);
 
         $list_users = [];

+ 1 - 7
main/gradebook/lib/be/abstractlink.class.php

@@ -563,15 +563,9 @@ abstract class AbstractLink implements GradebookItem
         return 'link';
     }
 
-    /* Seems to be not used anywhere */
-    public function get_not_created_links()
-    {
-        return null;
-    }
-
     public function get_all_links()
     {
-        return null;
+        return [];
     }
 
     public function add_linked_data()

+ 2 - 42
main/gradebook/lib/be/attendancelink.class.php

@@ -38,46 +38,6 @@ class AttendanceLink extends AbstractLink
         return false;
     }
 
-    /**
-     * Generate an array of attendances that a teacher hasn't created a link for.
-     *
-     * @return array 2-dimensional array - every element contains 2 subelements (id, name)
-     *
-     * @todo seems to be depracated
-     */
-    public function get_not_created_links()
-    {
-        return false;
-        if (empty($this->course_code)) {
-            die('Error in get_not_created_links() : course code not set');
-        }
-        $tbl_grade_links = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
-
-        $sql = 'SELECT att.id, att.name, att.attendance_qualify_title
-                FROM '.$this->get_attendance_table().' att
-                WHERE
-                    att.c_id = '.$this->course_id.' AND
-                    att.id NOT IN (
-                        SELECT ref_id FROM '.$tbl_grade_links.'
-                        WHERE
-                            type = '.LINK_ATTENDANCE.' AND
-                            course_code = "'.Database::escape_string($this->get_course_code()).'"
-                    )
-                AND att.session_id='.api_get_session_id().'';
-        $result = Database::query($sql);
-
-        $cats = [];
-        while ($data = Database::fetch_array($result)) {
-            if (isset($data['attendance_qualify_title']) && $data['attendance_qualify_title'] != '') {
-                $cats[] = [$data['id'], $data['attendance_qualify_title']];
-            } else {
-                $cats[] = [$data['id'], $data['name']];
-            }
-        }
-
-        return $cats;
-    }
-
     /**
      * Generate an array of all attendances available.
      *
@@ -86,7 +46,7 @@ class AttendanceLink extends AbstractLink
     public function get_all_links()
     {
         if (empty($this->course_code)) {
-            die('Error in get_not_created_links() : course code not set');
+            return [];
         }
         $tbl_attendance = $this->get_attendance_table();
         $session_id = api_get_session_id();
@@ -106,7 +66,7 @@ class AttendanceLink extends AbstractLink
                 $cats[] = [$data['id'], $data['name']];
             }
         }
-        $my_cats = isset($cats) ? $cats : null;
+        $my_cats = isset($cats) ? $cats : [];
 
         return $my_cats;
     }

+ 5 - 3
main/gradebook/lib/be/dropboxlink.class.php

@@ -31,9 +31,11 @@ class DropboxLink extends EvalLink
         // with the same title as the evaluation name
 
         $eval = $this->get_evaluation();
-        $sql = 'SELECT filename FROM '.$this->get_dropbox_table()
-            .' WHERE c_id = '.$this->course_id.' AND uploader_id = '.intval($stud_id)
-            ." AND title = '".Database::escape_string($eval->get_name())."'";
+        $sql = 'SELECT filename FROM '.$this->get_dropbox_table().' 
+                WHERE 
+                    c_id = '.$this->course_id.' AND 
+                    uploader_id = '.intval($stud_id)." AND 
+                    title = '".Database::escape_string($eval->get_name())."'";
 
         $result = Database::query($sql);
         if ($fileurl = Database::fetch_row($result)) {

+ 55 - 68
main/gradebook/lib/be/exerciselink.class.php

@@ -13,7 +13,7 @@ class ExerciseLink extends AbstractLink
 {
     private $course_info = null;
     private $exercise_table = null;
-    private $exercise_data = null;
+    private $exercise_data = [];
     private $is_hp;
 
     /**
@@ -29,59 +29,29 @@ class ExerciseLink extends AbstractLink
         }
     }
 
-    /**
-     * Generate an array of exercises that a teacher hasn't created a link for.
-     *
-     * @return array 2-dimensional array - every element contains 2 subelements (id, name)
-     */
-    public function get_not_created_links()
-    {
-        return false;
-
-        if (empty($this->course_code)) {
-            die('Error in get_not_created_links() : course code not set');
-        }
-        $tbl_grade_links = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
-
-        $sql = 'SELECT id, title FROM '.$this->get_exercise_table().' exe
-                WHERE id NOT IN (
-                    SELECT ref_id FROM '.$tbl_grade_links.'
-                    WHERE
-                        type = '.LINK_EXERCISE." AND
-                        course_code = '".$this->get_course_code()."'
-                ) AND
-                exe.c_id = ".$this->course_id;
-
-        $result = Database::query($sql);
-        $cats = [];
-        while ($data = Database::fetch_array($result)) {
-            $cats[] = [$data['id'], $data['title']];
-        }
-
-        return $cats;
-    }
-
     /**
      * Generate an array of all exercises available.
      *
+     * @param bool $getOnlyHotPotatoes
+     *
      * @return array 2-dimensional array - every element contains 2 subelements (id, name)
      */
     public function get_all_links($getOnlyHotPotatoes = false)
     {
         $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
-        $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
+        $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
         $exerciseTable = $this->get_exercise_table();
         $lpItemTable = Database::get_course_table(TABLE_LP_ITEM);
 
         $documentPath = api_get_path(SYS_COURSE_PATH).$this->course_code."/document";
         if (empty($this->course_code)) {
-            die('Error in get_not_created_links() : course code not set');
+            return [];
         }
-        $session_id = api_get_session_id();
-        if (empty($session_id)) {
+        $sessionId = api_get_session_id();
+        if (empty($sessionId)) {
             $session_condition = api_get_session_condition(0, true);
         } else {
-            $session_condition = api_get_session_condition($session_id, true, true);
+            $session_condition = api_get_session_condition($sessionId, true, true);
         }
 
         // @todo
@@ -102,7 +72,7 @@ class ExerciseLink extends AbstractLink
 
         $sql2 = "SELECT d.path as path, d.comment as comment, ip.visibility as visibility, d.id
                 FROM $TBL_DOCUMENT d 
-                INNER JOIN $TBL_ITEM_PROPERTY ip
+                INNER JOIN $tableItemProperty ip
                 ON (d.id = ip.ref AND d.c_id = ip.c_id)
                 WHERE
                     d.c_id = $this->course_id AND
@@ -179,14 +149,14 @@ class ExerciseLink extends AbstractLink
     public function has_results()
     {
         $tbl_stats = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
-        $session_id = api_get_session_id();
+        $sessionId = api_get_session_id();
         $course_id = api_get_course_int_id($this->get_course_code());
         $sql = "SELECT count(exe_id) AS number 
                 FROM $tbl_stats
                 WHERE
-                    session_id = $session_id AND
+                    session_id = $sessionId AND
                     c_id = $course_id AND
-                    exe_exo_id   = ".(int) $this->get_ref_id();
+                    exe_exo_id = ".$this->get_ref_id();
         $result = Database::query($sql);
         $number = Database::fetch_row($result);
 
@@ -211,21 +181,28 @@ class ExerciseLink extends AbstractLink
 
         /* the following query should be similar (in conditions) to the one used
         in exercise/exercise.php, look for note-query-exe-results marker*/
-        $session_id = $this->get_session_id();
+        $sessionId = $this->get_session_id();
         $courseId = $this->getCourseId();
+        $exerciseData = $this->get_exercise_data();
+        $exerciseId = isset($exerciseData['id']) ? $exerciseData['id'] : 0;
+
+        if (empty($exerciseId)) {
+            return null;
+        }
         $exercise = new Exercise($courseId);
-        $exercise->read($this->get_ref_id());
+        $exercise->read($exerciseData['id']);
+
         $stud_id = (int) $stud_id;
 
         if (!$this->is_hp) {
             if ($exercise->exercise_was_added_in_lp == false) {
                 $sql = "SELECT * FROM $tblStats
                         WHERE
-                            exe_exo_id = ".intval($this->get_ref_id())." AND
+                            exe_exo_id = ".$exerciseId." AND
                             orig_lp_id = 0 AND
                             orig_lp_item_id = 0 AND
                             status <> 'incomplete' AND
-                            session_id = $session_id AND
+                            session_id = $sessionId AND
                             c_id = $courseId
                         ";
             } else {
@@ -239,10 +216,10 @@ class ExerciseLink extends AbstractLink
                 $sql = "SELECT * 
                         FROM $tblStats
                         WHERE
-                            exe_exo_id = ".intval($this->get_ref_id())." AND
+                            exe_exo_id = ".$exerciseId." AND
                             orig_lp_id = $lpId AND
                             status <> 'incomplete' AND
-                            session_id = $session_id AND
+                            session_id = $sessionId AND
                             c_id = $courseId ";
             }
 
@@ -256,7 +233,7 @@ class ExerciseLink extends AbstractLink
                     ON (hp.exe_name = doc.path AND doc.c_id = hp.c_id)
                     WHERE
                         hp.c_id = $courseId AND                        
-                        doc.id = ".intval($this->get_ref_id());
+                        doc.id = ".$exerciseId;
 
             if (!empty($stud_id)) {
                 $sql .= " AND hp.exe_user_id = $stud_id ";
@@ -333,18 +310,19 @@ class ExerciseLink extends AbstractLink
     {
         // Status student
         $user_id = api_get_user_id();
-        $session_id = api_get_session_id();
+        $sessionId = api_get_session_id();
         $course_code = $this->get_course_code();
         $courseInfo = api_get_course_info($course_code);
         $courseId = $courseInfo['real_id'];
         $status_user = api_get_status_of_user_in_course($user_id, $courseId);
 
         $data = $this->get_exercise_data();
+        $exerciseId = $data['id'];
         $path = isset($data['path']) ? $data['path'] : '';
 
-        $url = api_get_path(WEB_CODE_PATH).'gradebook/exercise_jump.php?path='.$path.'&session_id='.$session_id.'&cidReq='.$this->get_course_code().'&gradebook=view&exerciseId='.$this->get_ref_id().'&type='.$this->get_type();
+        $url = api_get_path(WEB_CODE_PATH).'gradebook/exercise_jump.php?path='.$path.'&session_id='.$sessionId.'&cidReq='.$this->get_course_code().'&gradebook=view&exerciseId='.$exerciseId.'&type='.$this->get_type();
         if ((!api_is_allowed_to_edit() && $this->calc_score($user_id) == null) || $status_user != 1) {
-            $url .= '&doexercise='.$this->get_ref_id();
+            $url .= '&doexercise='.$exerciseId;
         }
 
         return $url;
@@ -358,6 +336,7 @@ class ExerciseLink extends AbstractLink
         $documentPath = api_get_path(SYS_COURSE_PATH).$this->course_code."/document";
         require_once api_get_path(SYS_CODE_PATH).'exercise/hotpotatoes.lib.php';
         $data = $this->get_exercise_data();
+
         if ($this->is_hp == 1) {
             if (isset($data['path'])) {
                 $title = GetQuizName($data['path'], $documentPath);
@@ -387,15 +366,9 @@ class ExerciseLink extends AbstractLink
      */
     public function is_valid_link()
     {
-        $sql = 'SELECT count(id) 
-                FROM '.$this->get_exercise_table().'
-                WHERE 
-                    c_id = '.$this->course_id.' AND 
-                    id = '.(int) $this->get_ref_id().' ';
-        $result = Database::query($sql);
-        $number = Database::fetch_row($result);
+        $exerciseData = $this->get_exercise_data();
 
-        return $number[0] != 0;
+        return !empty($exerciseData);
     }
 
     /**
@@ -461,38 +434,52 @@ class ExerciseLink extends AbstractLink
      */
     private function get_exercise_data()
     {
-        $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
+        $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
         if ($this->is_hp == 1) {
             $tbl_exercise = Database::get_course_table(TABLE_DOCUMENT);
         } else {
             $tbl_exercise = $this->get_exercise_table();
         }
 
-        $ref_id = intval($this->get_ref_id());
+        $exerciseId = $this->get_ref_id();
 
         if ($tbl_exercise == '') {
             return false;
-        } elseif (!isset($this->exercise_data)) {
+        } elseif (empty($this->exercise_data)) {
             if ($this->is_hp == 1) {
                 $sql = "SELECT * FROM $tbl_exercise ex
-                        INNER JOIN $TBL_ITEM_PROPERTY ip
+                        INNER JOIN $tableItemProperty ip
                         ON (ip.ref = ex.id AND ip.c_id = ex.c_id)
                         WHERE
                             ip.c_id = $this->course_id AND
                             ex.c_id = $this->course_id AND
-                            ip.ref = $ref_id AND
+                            ip.ref = $exerciseId AND
                             ip.tool = '".TOOL_DOCUMENT."' AND
                             ex.path LIKE '%htm%' AND
                             ex.path LIKE '%HotPotatoes_files%' AND
                             ip.visibility = 1";
+                $result = Database::query($sql);
+                $this->exercise_data = Database::fetch_array($result);
             } else {
                 $sql = 'SELECT * FROM '.$tbl_exercise.'
                         WHERE
                             c_id = '.$this->course_id.' AND
-                            id = '.$ref_id;
+                            id = '.$exerciseId;
+                $result = Database::query($sql);
+                $rows = Database::num_rows($result);
+
+                if (!empty($rows)) {
+                    $this->exercise_data = Database::fetch_array($result);
+                } else {
+                    // Try wit iid
+                    $sql = 'SELECT * FROM '.$tbl_exercise.'
+                            WHERE
+                                c_id = '.$this->course_id.' AND
+                                iid = '.$exerciseId;
+                    $result = Database::query($sql);
+                    $this->exercise_data = Database::fetch_array($result);
+                }
             }
-            $result = Database::query($sql);
-            $this->exercise_data = Database::fetch_array($result);
         }
 
         return $this->exercise_data;

+ 1 - 3
main/gradebook/lib/be/forumthreadlink.class.php

@@ -117,7 +117,7 @@ class ForumThreadLink extends AbstractLink
                 $cats[] = [$data['thread_id'], $data['thread_title']];
             }
         }
-        $my_cats = isset($cats) ? $cats : null;
+        $my_cats = isset($cats) ? $cats : [];
 
         return $my_cats;
     }
@@ -152,9 +152,7 @@ class ForumThreadLink extends AbstractLink
     {
         require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
         $threadInfo = get_thread_information('', $this->get_ref_id());
-
         $thread_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY);
-
         $sessionId = $this->get_session_id();
         $sessionCondition = api_get_session_condition(
             $sessionId,

+ 1 - 32
main/gradebook/lib/be/learnpathlink.class.php

@@ -24,37 +24,6 @@ class LearnpathLink extends AbstractLink
         $this->set_type(LINK_LEARNPATH);
     }
 
-    /**
-     * Generate an array of learnpaths that a teacher hasn't created a link for.
-     *
-     * @return array 2-dimensional array - every element contains 2 subelements (id, name)
-     */
-    public function get_not_created_links()
-    {
-        return false;
-        if (empty($this->course_code)) {
-            die('Error in get_not_created_links() : course code not set');
-        }
-
-        $tbl_grade_links = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
-
-        $sql = 'SELECT id, name FROM '.$this->get_learnpath_table().' lp
-				WHERE c_id = '.$this->course_id.' AND id NOT IN '
-            .' (SELECT ref_id FROM '.$tbl_grade_links
-            .' WHERE type = '.LINK_LEARNPATH
-            ." AND course_code = '".$this->get_course_code()."'"
-            .') AND lp.session_id='.api_get_session_id().'';
-
-        $result = Database::query($sql);
-
-        $cats = [];
-        while ($data = Database::fetch_array($result)) {
-            $cats[] = [$data['id'], $data['name']];
-        }
-
-        return $cats;
-    }
-
     /**
      * Generate an array of all learnpaths available.
      *
@@ -63,7 +32,7 @@ class LearnpathLink extends AbstractLink
     public function get_all_links()
     {
         if (empty($this->course_code)) {
-            die('Error in get_not_created_links() : course code not set');
+            return [];
         }
 
         $session_id = api_get_session_id();

+ 1 - 31
main/gradebook/lib/be/studentpublicationlink.class.php

@@ -71,36 +71,6 @@ class StudentPublicationLink extends AbstractLink
         return false;
     }
 
-    /**
-     * Generate an array of exercises that a teacher hasn't created a link for.
-     *
-     * @return array 2-dimensional array - every element contains 2 subelements (id, name)
-     */
-    public function get_not_created_links()
-    {
-        return false;
-        if (empty($this->course_code)) {
-            die('Error in get_not_created_links() : course code not set');
-        }
-        $tbl_grade_links = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
-
-        $sql = 'SELECT id, url from '.$this->get_studpub_table()
-            .' pup WHERE c_id = '.$this->course_id.' AND has_properties != '."''".' AND id NOT IN'
-            .' (SELECT ref_id FROM '.$tbl_grade_links
-            .' WHERE type = '.LINK_STUDENTPUBLICATION
-            ." AND course_code = '".Database::escape_string($this->get_course_code())."'"
-            .') AND pub.session_id='.api_get_session_id().'';
-
-        $result = Database::query($sql);
-
-        $cats = [];
-        while ($data = Database::fetch_array($result)) {
-            $cats[] = [$data['id'], $data['url']];
-        }
-
-        return $cats;
-    }
-
     /**
      * Generate an array of all exercises available.
      *
@@ -109,7 +79,7 @@ class StudentPublicationLink extends AbstractLink
     public function get_all_links()
     {
         if (empty($this->course_code)) {
-            die('Error in get_not_created_links() : course code not set');
+            return [];
         }
         $em = Database::getManager();
         $session = $em->find('ChamiloCoreBundle:Session', api_get_session_id());

+ 1 - 37
main/gradebook/lib/be/surveylink.class.php

@@ -90,43 +90,7 @@ class SurveyLink extends AbstractLink
             ];
         }
 
-        return isset($links) ? $links : null;
-    }
-
-    /**
-     * Generates an array of surveys that a teacher hasn't created a link for.
-     *
-     * @return array 2-dimensional array - every element contains 2 subelements (id, name)
-     */
-    public function get_not_created_links()
-    {
-        if (empty($this->course_code)) {
-            die('Error in get_not_created_links() : course code not set');
-        }
-        $tbl_grade_links = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
-
-        $sql = 'SELECT survey_id, title, code
-                FROM '.$this->get_survey_table().' AS srv
-                WHERE survey_id NOT IN
-                    (
-                    SELECT ref_id FROM '.$tbl_grade_links.'
-                    WHERE
-                        type = '.LINK_SURVEY.' AND
-                        course_code = "'.$this->get_course_code().'"
-                    )
-                    AND srv.session_id = '.api_get_session_id();
-
-        $result = Database::query($sql);
-
-        $links = [];
-        while ($data = Database::fetch_array($result)) {
-            $links[] = [
-                $data['survey_id'],
-                api_trunc_str($data['code'].': '.self::html_to_text($data['title']), 80),
-            ];
-        }
-
-        return $links;
+        return isset($links) ? $links : [];
     }
 
     /**

+ 0 - 1
main/gradebook/lib/fe/flatviewtable.class.php

@@ -417,7 +417,6 @@ class FlatViewTable extends SortableTable
 
         // step 1: generate columns: evaluations and links
         $header_names = $this->datagen->get_header_names($this->offset, $selectlimit);
-
         $userRowSpan = false;
         foreach ($header_names as $item) {
             if (is_array($item)) {

+ 1 - 1
main/gradebook/lib/fe/gradebooktable.class.php

@@ -1075,7 +1075,7 @@ class GradebookTable extends SortableTable
                 if (api_is_allowed_to_edit() && $show_message === false) {
                     if ($item->get_type() == 'presence') {
                         return '&nbsp;'
-                            .'<a href="gradebook_view_result.php?cidReq='.$item->get_course_code().'&amp;selecteval='.$item->get_id().'">'
+                            .'<a href="gradebook_view_result.php?cidReq='.$course_id.'&amp;selecteval='.$item->get_id().'">'
                             .$item->get_name()
                             .'</a>';
                     } else {

+ 9 - 2
main/gradebook/lib/flatview_data_generator.class.php

@@ -18,6 +18,7 @@ class FlatViewDataGenerator
     const FVDG_SORT_ASC = 4;
     const FVDG_SORT_DESC = 8;
     public $params;
+    /** @var Category */
     public $category;
 
     private $users;
@@ -171,6 +172,7 @@ class FlatViewDataGenerator
         $evaluationsAdded = [];
         if ($parent_id == 0 && !empty($allcat)) {
             // Means there are any subcategory
+            /** @var Category $sub_cat */
             foreach ($allcat as $sub_cat) {
                 $sub_cat_weight = round(100 * $sub_cat->get_weight() / $main_weight, 1);
                 $add_weight = " $sub_cat_weight %";
@@ -283,8 +285,13 @@ class FlatViewDataGenerator
     /**
      * Get actual array data.
      *
-     * @param int $users_count
-     * @param int $items_count
+     * @param int  $users_sorting
+     * @param int  $users_start
+     * @param null $users_count
+     * @param int  $items_start
+     * @param null $items_count
+     * @param bool $ignore_score_color
+     * @param bool $show_all
      *
      * @return array 2-dimensional array - each array contains the elements:
      *               0: user id

+ 162 - 64
main/group/group_category.php

@@ -11,6 +11,10 @@ $current_course_tool = TOOL_GROUP;
 // Notice for unauthorized people.
 api_protect_course_script(true);
 
+if (api_get_setting('allow_group_categories') == 'false') {
+    api_not_allowed(true);
+}
+
 $sessionId = api_get_session_id();
 
 if (!api_is_allowed_to_edit(false, true) ||
@@ -59,28 +63,23 @@ function check_groups_per_user($value)
     return true;
 }
 
-if (api_get_setting('allow_group_categories') === 'true') {
-    if (isset($_GET['id'])) {
-        $category = GroupManager::get_category($_GET['id']);
-        $nameTools = get_lang('EditGroupCategory').': '.$category['title'];
-    } else {
-        $nameTools = get_lang('AddCategory');
-        // Default values for new category
-        $category = [
-            'groups_per_user' => 1,
-            'doc_state' => GroupManager::TOOL_PRIVATE,
-            'work_state' => GroupManager::TOOL_PRIVATE,
-            'wiki_state' => GroupManager::TOOL_PRIVATE,
-            'chat_state' => GroupManager::TOOL_PRIVATE,
-            'calendar_state' => GroupManager::TOOL_PRIVATE,
-            'announcements_state' => GroupManager::TOOL_PRIVATE,
-            'forum_state' => GroupManager::TOOL_PRIVATE,
-            'max_student' => 0,
-        ];
-    }
-} else {
+if (isset($_GET['id'])) {
     $category = GroupManager::get_category($_GET['id']);
-    $nameTools = get_lang('PropModify');
+    $nameTools = get_lang('EditGroupCategory').': '.$category['title'];
+} else {
+    $nameTools = get_lang('AddCategory');
+    // Default values for new category
+    $category = [
+        'groups_per_user' => 1,
+        'doc_state' => GroupManager::TOOL_PRIVATE,
+        'work_state' => GroupManager::TOOL_PRIVATE,
+        'wiki_state' => GroupManager::TOOL_PRIVATE,
+        'chat_state' => GroupManager::TOOL_PRIVATE,
+        'calendar_state' => GroupManager::TOOL_PRIVATE,
+        'announcements_state' => GroupManager::TOOL_PRIVATE,
+        'forum_state' => GroupManager::TOOL_PRIVATE,
+        'max_student' => 0,
+    ];
 }
 
 $htmlHeadXtra[] = '<script>
@@ -108,61 +107,78 @@ if (isset($_GET['id'])) {
     // Checks if the field was created in the table Category. It creates it if is neccesary
     $table_category = Database::get_course_table(TABLE_GROUP_CATEGORY);
     if (!Database::query("SELECT wiki_state FROM $table_category WHERE c_id = $course_id")) {
-        Database::query("ALTER TABLE $table_category ADD wiki_state tinyint(3) UNSIGNED NOT NULL default '1' WHERE c_id = $course_id");
+        $sql = "ALTER TABLE $table_category ADD wiki_state tinyint(3) UNSIGNED NOT NULL default '1'
+                WHERE c_id = $course_id";
+        Database::query($sql);
     }
     // Create a new category
     $action = 'add_category';
     $form = new FormValidator('group_category');
 }
 
-// If categories allowed, show title & description field
-if (api_get_setting('allow_group_categories') == 'true') {
-    $form->addElement('header', $nameTools);
-    $form->addElement('html', '<div class="row"><div class="col-md-6">');
-    $form->addText('title', get_lang('Title'));
+$form->addElement('header', $nameTools);
+$form->addElement('html', '<div class="row"><div class="col-md-6">');
+$form->addText('title', get_lang('Title'));
 
-    // Groups per user
-    $possible_values = [];
-    for ($i = 1; $i <= 10; $i++) {
-        $possible_values[$i] = $i;
-    }
-    $possible_values[GroupManager::GROUP_PER_MEMBER_NO_LIMIT] = get_lang('All');
-    $group = [
-        $form->createElement('select', 'groups_per_user', null, $possible_values),
-        $form->createElement('static', null, null, get_lang('QtyOfUserCanSubscribe_PartAfterNumber')),
-    ];
-    $form->addGroup($group, 'limit_group', get_lang('QtyOfUserCanSubscribe_PartBeforeNumber'), null, false);
-    $form->addRule('limit_group', get_lang('MaxGroupsPerUserInvalid'), 'callback', 'check_groups_per_user');
+// Groups per user
+$possible_values = [];
+for ($i = 1; $i <= 10; $i++) {
+    $possible_values[$i] = $i;
+}
+$possible_values[GroupManager::GROUP_PER_MEMBER_NO_LIMIT] = get_lang('All');
+$group = [
+    $form->createElement('select', 'groups_per_user', null, $possible_values),
+    $form->createElement('static', null, null, get_lang('QtyOfUserCanSubscribe_PartAfterNumber')),
+];
+$form->addGroup($group, 'limit_group', get_lang('QtyOfUserCanSubscribe_PartBeforeNumber'), null, false);
+$form->addRule('limit_group', get_lang('MaxGroupsPerUserInvalid'), 'callback', 'check_groups_per_user');
 
-    // Members per group
-    $group = [
-        $form->createElement('radio', 'max_member_no_limit', get_lang('GroupLimit'), get_lang('NoLimit'), GroupManager::MEMBER_PER_GROUP_NO_LIMIT),
-        $form->createElement('radio', 'max_member_no_limit', null, get_lang('MaximumOfParticipants'), 1, ['id' => 'max_member_selected']),
-        $form->createElement('text', 'max_member', null, ['class' => 'span1', 'id' => 'max_member']),
-        $form->createElement('static', null, null, ' '.get_lang('GroupPlacesThis')),
-    ];
-    $form->addGroup($group, 'max_member_group', get_lang('GroupLimit'), null, false);
-    $form->addRule('max_member_group', get_lang('InvalidMaxNumberOfMembers'), 'callback', 'check_max_number_of_members');
+$form->addElement('html', '</div>');
 
-    $form->addElement('html', '</div>');
+$form->addElement('html', '<div class="col-md-6">');
+$form->addElement('textarea', 'description', get_lang('Description'), ['rows' => 6]);
+$form->addElement('html', '</div>');
 
-    $form->addElement('html', '<div class="col-md-6">');
-    // Description
-    $form->addElement('textarea', 'description', get_lang('Description'), ['rows' => 6]);
-    $form->addElement('html', '</div>');
-    $form->addElement('html', '</div>');
-} else {
-    $form->addElement('hidden', 'title');
-    $form->addElement('hidden', 'description');
-}
+$form->addElement('html', '<div class="col-md-12">');
+$form->addElement('header', '');
+$form->addElement('html', '</div>');
 
-$form->addElement('header', get_lang('DefaultSettingsForNewGroups'));
-$form->addElement('hidden', 'action');
 $form->addElement('html', '<div class="col-md-6">');
 
+// Members per group
+$group = [
+    $form->createElement(
+        'radio',
+        'max_member_no_limit',
+        get_lang('GroupLimit'),
+        get_lang('NoLimit'),
+        GroupManager::MEMBER_PER_GROUP_NO_LIMIT
+    ),
+    $form->createElement(
+        'radio',
+        'max_member_no_limit',
+        null,
+        get_lang('MaximumOfParticipants'),
+        1,
+        ['id' => 'max_member_selected']
+    ),
+    $form->createElement('text', 'max_member', null, ['class' => 'span1', 'id' => 'max_member']),
+    $form->createElement('static', null, null, ' '.get_lang('GroupPlacesThis')),
+];
+$form->addGroup($group, 'max_member_group', get_lang('GroupLimit'), null, false);
+$form->addRule('max_member_group', get_lang('InvalidMaxNumberOfMembers'), 'callback', 'check_max_number_of_members');
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
 // Self registration
 $group = [
-    $form->createElement('checkbox', 'self_reg_allowed', get_lang('GroupSelfRegistration'), get_lang('GroupAllowStudentRegistration'), 1),
+    $form->createElement(
+        'checkbox',
+        'self_reg_allowed',
+        get_lang('GroupSelfRegistration'),
+        get_lang('GroupAllowStudentRegistration'),
+        1
+    ),
     $form->createElement('checkbox', 'self_unreg_allowed', null, get_lang('GroupAllowStudentUnregistration'), 1),
 ];
 $form->addGroup(
@@ -172,6 +188,15 @@ $form->addGroup(
     null,
     false
 );
+$form->addElement('html', '</div>');
+
+$form->addElement('hidden', 'action');
+
+$form->addElement('html', '<div class="col-md-12">');
+$form->addElement('header', get_lang('DefaultSettingsForNewGroups'));
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
 
 // Documents settings.
 $group = [
@@ -187,6 +212,54 @@ $form->addGroup(
     false
 );
 
+$allowDocumentGroupAccess = api_get_configuration_value('group_category_document_access');
+if ($allowDocumentGroupAccess) {
+    $form->addElement('html', '</div>');
+    $form->addElement('html', '<div class="col-md-6">');
+    $group = [
+        $form->createElement(
+            'radio',
+            'document_access',
+            null,
+            get_lang('DocumentGroupShareMode'),
+            GroupManager::DOCUMENT_MODE_SHARE
+        ),
+        $form->createElement(
+            'radio',
+            'document_access',
+            get_lang('GroupDocument'),
+            get_lang('DocumentGroupCollaborationMode'),
+            GroupManager::DOCUMENT_MODE_COLLABORATION
+        ),
+        $form->createElement(
+            'radio',
+            'document_access',
+            null,
+            get_lang('DocumentGroupReadOnlyMode'),
+            GroupManager::DOCUMENT_MODE_READ_ONLY
+        ),
+    ];
+    $form->addGroup(
+        $group,
+        '',
+        Display::return_icon(
+            'folder.png',
+            get_lang('GroupDocumentAccess')
+        ).'<span>'.get_lang('GroupDocumentAccess').'</span>',
+        null,
+        false
+    );
+    $form->addElement('html', '</div>');
+}
+
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-12">');
+$form->addElement('header', '');
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
+
 // Work settings.
 $group = [
     $form->createElement('radio', 'work_state', get_lang('GroupWork'), get_lang('NotAvailable'), GroupManager::TOOL_NOT_AVAILABLE),
@@ -201,6 +274,10 @@ $form->addGroup(
     false
 );
 
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
+
 // Calendar settings.
 $group = [
     $form->createElement('radio', 'calendar_state', get_lang('GroupCalendar'), get_lang('NotAvailable'), GroupManager::TOOL_NOT_AVAILABLE),
@@ -214,8 +291,12 @@ $form->addGroup(
     null,
     false
 );
+$form->addElement('html', '</div>');
 
+$form->addElement('html', '<div class="col-md-12">');
+$form->addElement('header', '');
 $form->addElement('html', '</div>');
+
 $form->addElement('html', '<div class="col-md-6">');
 
 // Announcements settings.
@@ -232,6 +313,10 @@ $form->addGroup(
     false
 );
 
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
+
 // Forum settings.
 $group = [
     $form->createElement('radio', 'forum_state', get_lang('GroupForum'), get_lang('NotAvailable'), GroupManager::TOOL_NOT_AVAILABLE),
@@ -246,6 +331,14 @@ $form->addGroup(
     false
 );
 
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-12">');
+$form->addElement('header', '');
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
+
 // Wiki settings.
 $group = [
     $form->createElement('radio', 'wiki_state', get_lang('GroupWiki'), get_lang('NotAvailable'), GroupManager::TOOL_NOT_AVAILABLE),
@@ -259,6 +352,9 @@ $form->addGroup(
     null,
     false
 );
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
 
 // Chat settings.
 $group = [
@@ -313,13 +409,14 @@ if ($form->validate()) {
                 $self_reg_allowed,
                 $self_unreg_allowed,
                 $max_member,
-                $values['groups_per_user']
+                $values['groups_per_user'],
+                isset($values['document_access']) ? $values['document_access'] : 0
             );
             Display::addFlash(Display::return_message(get_lang('GroupPropertiesModified')));
             header("Location: ".$currentUrl."&category=".$values['id']);
             exit;
         case 'add_category':
-            GroupManager :: create_category(
+            GroupManager::create_category(
                 $values['title'],
                 $values['description'],
                 $values['doc_state'],
@@ -332,7 +429,8 @@ if ($form->validate()) {
                 $self_reg_allowed,
                 $self_unreg_allowed,
                 $max_member,
-                $values['groups_per_user']
+                $values['groups_per_user'],
+                isset($values['document_access']) ? $values['document_access'] : 0
             );
             Display::addFlash(Display::return_message(get_lang('CategoryCreated')));
             header("Location: ".$currentUrl);

+ 108 - 13
main/group/settings.php

@@ -52,6 +52,18 @@ if (api_get_setting('allow_group_categories') == 'true') {
 } else {
     $form->addHidden('category_id', 0);
 }
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
+$form->addElement('textarea', 'description', get_lang('Description'));
+
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-12">');
+$form->addElement('header', '');
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
 
 // Members per group
 $group = [
@@ -65,15 +77,6 @@ $form->addRule('max_member_group', get_lang('InvalidMaxNumberOfMembers'), 'callb
 
 $form->addElement('html', '</div>');
 
-$form->addElement('html', '<div class="col-md-6">');
-// Description
-$form->addElement('textarea', 'description', get_lang('Description'));
-$form->addElement('html', '</div>');
-
-$form->addElement('html', '<div class="col-md-12">');
-$form->addElement('header', get_lang('DefaultSettingsForNewGroups'));
-$form->addElement('html', '</div>');
-
 $form->addElement('html', '<div class="col-md-6">');
 
 // Self registration
@@ -89,6 +92,13 @@ $form->addGroup(
     false
 );
 
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-12">');
+$form->addElement('header', get_lang('DefaultSettingsForNewGroups'));
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
 // Documents settings
 $group = [
     $form->createElement('radio', 'doc_state', get_lang('GroupDocument'), get_lang('NotAvailable'), GroupManager::TOOL_NOT_AVAILABLE),
@@ -103,12 +113,71 @@ $form->addGroup(
     false
 );
 
+$allowDocumentGroupAccess = api_get_configuration_value('group_document_access');
+if ($allowDocumentGroupAccess) {
+    $form->addElement('html', '</div>');
+    $form->addElement('html', '<div class="col-md-6">');
+    $group = [
+        $form->createElement(
+            'radio',
+            'document_access',
+            null,
+            get_lang('DocumentGroupShareMode'),
+            GroupManager::DOCUMENT_MODE_SHARE
+        ),
+        $form->createElement(
+            'radio',
+            'document_access',
+            get_lang('GroupDocument'),
+            get_lang('DocumentGroupCollaborationMode'),
+            GroupManager::DOCUMENT_MODE_COLLABORATION
+        ),
+        $form->createElement(
+            'radio',
+            'document_access',
+            null,
+            get_lang('DocumentGroupReadOnlyMode'),
+            GroupManager::DOCUMENT_MODE_READ_ONLY
+        ),
+    ];
+    $form->addGroup(
+        $group,
+        '',
+        Display::return_icon(
+            'folder.png',
+            get_lang('GroupDocumentAccess')
+        ).'<span>'.get_lang('GroupDocumentAccess').'</span>',
+        null,
+        false
+    );
+    $form->addElement('html', '</div>');
+
+    $form->addElement('html', '<div class="col-md-12">');
+    $form->addElement('header', '');
+    $form->addElement('html', '</div>');
+
+    $form->addElement('html', '<div class="col-md-6">');
+}
+
 // Work settings
 $group = [
-    $form->createElement('radio', 'work_state', get_lang('GroupWork'), get_lang('NotAvailable'), GroupManager::TOOL_NOT_AVAILABLE),
+    $form->createElement(
+        'radio',
+        'work_state',
+        get_lang('GroupWork'),
+        get_lang('NotAvailable'),
+        GroupManager::TOOL_NOT_AVAILABLE
+    ),
     $form->createElement('radio', 'work_state', null, get_lang('Public'), GroupManager::TOOL_PUBLIC),
     $form->createElement('radio', 'work_state', null, get_lang('Private'), GroupManager::TOOL_PRIVATE),
 ];
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-12">');
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
+
 $form->addGroup(
     $group,
     '',
@@ -123,6 +192,10 @@ $group = [
     $form->createElement('radio', 'calendar_state', null, get_lang('Public'), GroupManager::TOOL_PUBLIC),
     $form->createElement('radio', 'calendar_state', null, get_lang('Private'), GroupManager::TOOL_PRIVATE),
 ];
+
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
 $form->addGroup(
     $group,
     '',
@@ -132,6 +205,11 @@ $form->addGroup(
 );
 
 $form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-12">');
+$form->addElement('header', '');
+$form->addElement('html', '</div>');
+
 $form->addElement('html', '<div class="col-md-6">');
 
 // Announcements settings
@@ -140,6 +218,7 @@ $group = [
     $form->createElement('radio', 'announcements_state', null, get_lang('Public'), GroupManager::TOOL_PUBLIC),
     $form->createElement('radio', 'announcements_state', null, get_lang('Private'), GroupManager::TOOL_PRIVATE),
 ];
+
 $form->addGroup(
     $group,
     '',
@@ -148,6 +227,10 @@ $form->addGroup(
     false
 );
 
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
+
 // Forum settings
 $group = [
     $form->createElement('radio', 'forum_state', get_lang('GroupForum'), get_lang('NotAvailable'), GroupManager::TOOL_NOT_AVAILABLE),
@@ -162,6 +245,14 @@ $form->addGroup(
     false
 );
 
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-12">');
+$form->addElement('header', '');
+$form->addElement('html', '</div>');
+
+$form->addElement('html', '<div class="col-md-6">');
+
 // Wiki settings
 $group = [
     $form->createElement('radio', 'wiki_state', get_lang('GroupWiki'), get_lang('NotAvailable'), GroupManager::TOOL_NOT_AVAILABLE),
@@ -176,6 +267,9 @@ $form->addGroup(
     false
 );
 
+$form->addElement('html', '</div>');
+$form->addElement('html', '<div class="col-md-6">');
+
 // Chat settings
 $group = [
     $form->createElement('radio', 'chat_state', get_lang('Chat'), get_lang('NotAvailable'), GroupManager::TOOL_NOT_AVAILABLE),
@@ -221,7 +315,8 @@ if ($form->validate()) {
         $values['chat_state'],
         $self_registration_allowed,
         $self_unregistration_allowed,
-        $categoryId
+        $categoryId,
+        isset($values['document_access']) ? $values['document_access'] : 0
     );
     if (isset($_POST['group_members']) &&
         count($_POST['group_members']) > $max_member &&
@@ -231,7 +326,7 @@ if ($form->validate()) {
         header('Location: group.php?'.api_get_cidreq(true, false));
     } else {
         Display::addFlash(Display::return_message(get_lang('GroupSettingsModified'), 'success'));
-        header('Location: group.php?'.api_get_cidreq(true, false).'&category='.$categoryId);
+        header('Location: group.php?'.api_get_cidreq(true, false).'&category='.$cat['id']);
     }
     exit;
 }
@@ -256,7 +351,7 @@ if (!empty($_GET['keyword']) && !empty($_GET['submit'])) {
     echo '<br/>'.get_lang('SearchResultsFor').' <span style="font-style: italic ;"> '.$keyword_name.' </span><br>';
 }
 
-Display :: display_header($nameTools, 'Group');
+Display::display_header($nameTools, 'Group');
 
 $form->setDefaults($defaults);
 echo GroupManager::getSettingBar('settings');

+ 1 - 1
main/inc/ajax/admin.ajax.php

@@ -156,7 +156,7 @@ function check_system_version()
         );
 
         // The number of sessions
-        $number_of_sessions = Statistics::countSessions();
+        $number_of_sessions = SessionManager::count_sessions(api_get_current_access_url_id());
         $packager = api_get_configuration_value('packager');
         if (empty($packager)) {
             $packager = 'chamilo';

+ 3 - 0
main/inc/ajax/document.ajax.php

@@ -40,6 +40,9 @@ switch ($action) {
             $groupInfo = GroupManager::get_group_properties(api_get_group_id());
             // Only course admin or group members allowed
             if ($is_allowed_to_edit || GroupManager::is_user_in_group(api_get_user_id(), $groupInfo)) {
+                if (!GroupManager::allowUploadEditDocument(api_get_user_id(), api_get_course_int_id(), $groupInfo)) {
+                    exit;
+                }
             } else {
                 exit;
             }

+ 25 - 6
main/inc/ajax/exercise.ajax.php

@@ -357,15 +357,34 @@ switch ($action) {
     case 'add_question_to_reminder':
         /** @var Exercise $objExercise */
         $objExercise = Session::read('objExercise');
-        if (empty($objExercise)) {
+        $exeId = isset($_REQUEST['exe_id']) ? $_REQUEST['exe_id'] : 0;
+
+        if (empty($objExercise) || empty($exeId)) {
             echo 0;
             exit;
         } else {
-            $objExercise->editQuestionToRemind(
-                $_REQUEST['exe_id'],
-                $_REQUEST['question_id'],
-                $_REQUEST['action']
-            );
+            $option = isset($_GET['option']) ? $_GET['option'] : '';
+            switch ($option) {
+                case 'add_all':
+                    $questionListInSession = Session::read('questionList');
+                    $objExercise->addAllQuestionToRemind(
+                        $exeId,
+                        $questionListInSession
+                    );
+                    break;
+                case 'remove_all':
+                    $objExercise->removeAllQuestionToRemind(
+                        $exeId
+                    );
+                    break;
+                default:
+                    $objExercise->editQuestionToRemind(
+                        $exeId,
+                        $_REQUEST['question_id'],
+                        $_REQUEST['action']
+                    );
+                    break;
+            }
         }
         break;
     case 'save_exercise_by_now':

+ 2 - 3
main/inc/ajax/lp.ajax.php

@@ -323,9 +323,9 @@ switch ($action) {
             echo '1';
         } else {
             if (!empty($lp->error)) {
-                echo addslashes($lp->error);
+                echo $lp->error;
             } else {
-                echo addslashes(get_lang('LearnpathPrereqNotCompleted'));
+                echo get_lang('LearnpathPrereqNotCompleted');
             }
         }
         $lp->error = '';
@@ -392,7 +392,6 @@ class LP_item_order_list
         }
     }
 }
-
 class LP_item_order_item
 {
     public $id = 0;

+ 2 - 4
main/inc/ajax/model.ajax.php

@@ -1229,7 +1229,7 @@ switch ($action) {
         break;
     case 'get_exercise_results_report':
         // Used inside ExerciseLib::get_exam_results_data()
-        $documentPath = api_get_path(SYS_COURSE_PATH).$courseInfo['path']."/document";
+        $documentPath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
         $sessionId = api_get_session_id();
 
         $columns = [
@@ -1277,7 +1277,6 @@ switch ($action) {
 
             $overwriteColumnHeaderExport['session_access_start_date'] = get_lang('SessionStartDate');
             $overwriteColumnHeaderExport['exe_date'] = get_lang('StartDate');
-
             $overwriteColumnHeaderExport['score_percentage'] = get_lang('Score').' - '.get_lang('Percentage');
             $overwriteColumnHeaderExport['only_score'] = get_lang('Score').' - '.get_lang('ScoreNote');
             $overwriteColumnHeaderExport['total'] = get_lang('Score').' - '.get_lang('ScoreTest');
@@ -2171,7 +2170,6 @@ if (in_array($action, $allowed_actions)) {
 
     if ($operation && $operation == 'excel') {
         $j = 1;
-
         $array = [];
         if (empty($column_names)) {
             $column_names = $columns;
@@ -2179,7 +2177,7 @@ if (in_array($action, $allowed_actions)) {
 
         // Headers
         foreach ($column_names as $col) {
-            // Ovewrite titles
+            // Overwrite titles
             if (isset($overwriteColumnHeaderExport[$col])) {
                 $col = $overwriteColumnHeaderExport[$col];
             }

+ 8 - 0
main/inc/ajax/myspace.ajax.php

@@ -7,6 +7,14 @@
 require_once __DIR__.'/../global.inc.php';
 $action = $_GET['a'];
 
+// Access restrictions.
+$is_allowedToTrack = api_is_platform_admin(true, true) ||
+    api_is_allowed_to_create_course() || api_is_course_tutor();
+
+if (!$is_allowedToTrack) {
+    exit;
+}
+
 switch ($action) {
     // At this date : 23/02/2017, a minor review can't determine where is used this case 'access_detail'
     case 'access_detail':

+ 9 - 3
main/inc/ajax/session.ajax.php

@@ -1,6 +1,9 @@
 <?php
 /* For licensing terms, see /license.txt */
 
+use Chamilo\UserBundle\Entity\Repository\UserRepository;
+use Chamilo\UserBundle\Entity\User;
+
 /**
  * Responses to AJAX calls.
  */
@@ -172,7 +175,8 @@ switch ($action) {
         }
         break;
     case 'search_general_coach':
-        header('Content-Type: application/json');
+        SessionManager::protectSession(null, false);
+        api_protect_limit_for_session_admin();
 
         if (api_is_anonymous()) {
             echo '';
@@ -184,9 +188,10 @@ switch ($action) {
         ];
 
         $entityManager = Database::getManager();
+        /** @var UserRepository $usersRepo */
         $usersRepo = $entityManager->getRepository('ChamiloUserBundle:User');
-        $users = $usersRepo->searchUsersByStatus($_GET['q'], COURSEMANAGER);
-
+        $users = $usersRepo->searchUsersByStatus($_GET['q'], COURSEMANAGER, api_get_current_access_url_id());
+        /** @var User $user */
         foreach ($users as $user) {
             $list['items'][] = [
                 'id' => $user->getId(),
@@ -194,6 +199,7 @@ switch ($action) {
             ];
         }
 
+        header('Content-Type: application/json');
         echo json_encode($list);
         break;
     case 'get_courses_inside_session':

+ 94 - 2
main/inc/ajax/statistics.ajax.php

@@ -9,12 +9,104 @@ require_once __DIR__.'/../global.inc.php';
 api_protect_admin_script();
 
 $action = isset($_REQUEST['a']) ? $_REQUEST['a'] : null;
+$sessionDuration = isset($_GET['session_duration']) ? (int) $_GET['session_duration'] : 0;
+$exportFormat = isset($_REQUEST['export_format']) ? $_REQUEST['export_format'] : 'csv';
+$operation = isset($_REQUEST['oper']) ? $_REQUEST['oper'] : false;
+$order = isset($_REQUEST['sord']) && in_array($_REQUEST['sord'], ['asc', 'desc']) ? $_REQUEST['sord'] : 'asc';
 
 switch ($action) {
+    case 'get_user_session':
+        $list = [];
+
+        $urlList = UrlManager::get_url_data("url $order");
+        $sessionUrl = api_get_path(WEB_CODE_PATH).'session/resume_session.php?id_session=';
+
+        $start = isset($_GET['start']) ? Database::escape_string(api_get_utc_datetime($_GET['start'])) : api_get_utc_datetime();
+        $end = isset($_GET['end']) ? Database::escape_string(api_get_utc_datetime($_GET['end'])) : api_get_utc_datetime();
+
+        if (!empty($operation)) {
+            $list[] = [
+                'URL',
+                get_lang('Session'),
+                get_lang('Course'),
+                get_lang('CountUsers'),
+            ];
+        }
+
+        $courseListInfo = [];
+        foreach ($urlList as $url) {
+            $urlId = $url['id'];
+            $sessionList = SessionManager::get_sessions_list([], [], null, null, $urlId);
+            foreach ($sessionList as $session) {
+                $sessionId = $session['id'];
+                $row = [];
+                $row['url'] = $url['url'];
+                $row['session'] = Display::url(
+                    $session['name'],
+                    $sessionUrl.$sessionId
+                );
+
+                if (!empty($operation)) {
+                    $row['session'] = strip_tags($row['session']);
+                }
+
+                $courseList = SessionManager::getCoursesInSession($sessionId);
+                foreach ($courseList as $courseId) {
+                    if (!isset($courseListInfo[$courseId])) {
+                        $courseListInfo[$courseId] = $courseInfo = api_get_course_info_by_id($courseId);
+                    } else {
+                        $courseInfo = $courseListInfo[$courseId];
+                    }
+
+                    $row['course'] = $courseInfo['title'];
+
+                    $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
+                    $sql = "SELECT
+                            count(DISTINCT user_id) count
+                            FROM $table
+                            WHERE
+                                login_course_date >= '$start' AND  
+                                logout_course_date <= '$end' AND     
+                                c_id = $courseId AND
+                                session_id = '$sessionId' ";
+
+                    $result = Database::query($sql);
+                    $result = Database::fetch_array($result);
+                    $row['count'] = $result['count'];
+                }
+                $list[] = $row;
+            }
+        }
+
+        if (!empty($operation)) {
+            $fileName = !empty($action) ? get_lang('PortalUserSessionStats').'_'.api_get_local_time() : 'report';
+            switch ($exportFormat) {
+                case 'xls':
+                    Export::arrayToXls($list, $fileName);
+                    break;
+                case 'xls_html':
+                    //TODO add date if exists
+                    $browser = new Browser();
+                    if ($browser->getPlatform() == Browser::PLATFORM_WINDOWS) {
+                        Export::export_table_xls_html($list, $fileName, 'ISO-8859-15');
+                    } else {
+                        Export::export_table_xls_html($list, $fileName);
+                    }
+                    break;
+                case 'csv':
+                default:
+                    Export::arrayToCsv($list, $fileName);
+                    break;
+            }
+        }
+
+        echo json_encode($list);
+        break;
+        break;
     case 'recentlogins':
         header('Content-type: application/json');
         $list = [];
-        $all = Statistics::getRecentLoginStats();
+        $all = Statistics::getRecentLoginStats(false, $sessionDuration);
         foreach ($all as $tick => $tock) {
             $list['labels'][] = $tick;
         }
@@ -39,7 +131,7 @@ switch ($action) {
         $list['datasets'][1]['pointHighlightFill'] = "#fff";
         $list['datasets'][1]['pointHighlightStroke'] = "rgba(0,204,0,1)";
 
-        $distinct = Statistics::getRecentLoginStats(true);
+        $distinct = Statistics::getRecentLoginStats(true, $sessionDuration);
         foreach ($distinct as $tick => $tock) {
             $list['datasets'][1]['data'][] = $tock;
         }

+ 14 - 6
main/inc/introductionSection.inc.php

@@ -30,7 +30,6 @@ use Chamilo\CourseBundle\Entity\CToolIntro;
  * @package chamilo.include
  */
 $em = Database::getManager();
-$TBL_INTRODUCTION = Database::get_course_table(TABLE_TOOL_INTRO);
 $intro_editAllowed = $is_allowed_to_edit = api_is_allowed_to_edit();
 $session_id = api_get_session_id();
 
@@ -59,7 +58,7 @@ $config = [
     'Height' => '300',
 ];
 
-$form->addHtmlEditor('intro_content', null, false, false, $config, true);
+$form->addHtmlEditor('intro_content', null, false, false, $config);
 $form->addButtonSave(get_lang('SaveIntroText'), 'intro_cmdUpdate');
 
 /* INTRODUCTION MICRO MODULE - COMMANDS SECTION (IF ALLOWED) */
@@ -119,7 +118,6 @@ $toolIntro = $em
     ->findOneBy(['cId' => $course_id, 'id' => $moduleId, 'sessionId' => 0]);
 
 $intro_content = $toolIntro ? $toolIntro->getIntroText() : '';
-
 if ($session_id) {
     /** @var CToolIntro $toolIntro */
     $toolIntro = $em
@@ -127,7 +125,6 @@ if ($session_id) {
         ->findOneBy(['cId' => $course_id, 'id' => $moduleId, 'sessionId' => $session_id]);
 
     $introSessionContent = $toolIntro && $toolIntro->getIntroText() ? $toolIntro->getIntroText() : '';
-
     $intro_content = $introSessionContent ?: $intro_content;
 }
 
@@ -140,6 +137,13 @@ if (api_get_configuration_value('course_introduction_html_strict_filtering')) {
     $userStatus = COURSEMANAGER;
 }
 
+// Ignore editor.css
+$cssEditor = api_get_path(WEB_CSS_PATH).'editor.css';
+$linkToReplace = [
+    '<link href="'.$cssEditor.'" rel="stylesheet" type="text/css" />',
+    '<link href="'.$cssEditor.'" media="screen" rel="stylesheet" type="text/css" />',
+];
+$intro_content = str_replace($linkToReplace, '', $intro_content);
 $intro_content = Security::remove_XSS($intro_content, $userStatus);
 
 /* Determines the correct display */
@@ -340,8 +344,12 @@ if ($intro_dispCommand) {
     }
 }
 
-$introduction_section .= '<div class="col-md-12">';
+$nameSection = get_lang('AddCustomCourseIntro');
+if ($moduleId != 'course_homepage') {
+    $nameSection = get_lang('AddCustomToolsIntro');
+}
 
+$introduction_section .= '<div class="col-md-12">';
 if ($intro_dispDefault) {
     if (!empty($intro_content)) {
         $introduction_section .= '<div class="page-course">';
@@ -350,7 +358,7 @@ if ($intro_dispDefault) {
     } else {
         if (api_is_allowed_to_edit()) {
             $introduction_section .= '<div class="help-course">';
-            $introduction_section .= get_lang('AddCustomCourseIntro').' '.$textIntro;
+            $introduction_section .= $nameSection.' '.$textIntro;
             $introduction_section .= '</div>';
         }
     }

+ 2 - 4
main/inc/lib/AnnouncementEmail.php

@@ -30,9 +30,8 @@ class AnnouncementEmail
         $this->session_id = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
 
         if (is_numeric($announcementId)) {
-            $announcementId = AnnouncementManager::get_by_id($courseInfo['real_id'], $announcementId);
+            $this->announcement = AnnouncementManager::get_by_id($courseInfo['real_id'], $announcementId);
         }
-        $this->announcement = $announcementId;
         $this->logger = $logger;
     }
 
@@ -280,8 +279,7 @@ class AnnouncementEmail
      *
      * @param bool $sendToUsersInSession
      * @param bool $sendToDrhUsers       send a copy of the message to the DRH users
-     * @param int  $senderId
-     *                                   related to the main user
+     * @param int  $senderId             related to the main user
      */
     public function send($sendToUsersInSession = false, $sendToDrhUsers = false, $senderId = 0)
     {

+ 11 - 11
main/inc/lib/AnnouncementManager.php

@@ -45,8 +45,8 @@ class AnnouncementManager
                 $tags[] = "((extra_".$extra['variable']."))";
             }
         }
-
-        if (!empty(api_get_session_id())) {
+        $sessionId = api_get_session_id();
+        if (!empty($sessionId)) {
             $tags[] = '((coaches))';
             $tags[] = '((general_coach))';
             $tags[] = '((general_coach_email))';
@@ -798,7 +798,7 @@ class AnnouncementManager
         $courseInfo = api_get_course_info();
         $courseId = api_get_course_int_id();
         $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
-        $tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT);
+        $table = Database::get_course_table(TABLE_ANNOUNCEMENT);
         $id = intval($id);
 
         $params = [
@@ -807,7 +807,7 @@ class AnnouncementManager
         ];
 
         Database::update(
-            $tbl_announcement,
+            $table,
             $params,
             ['c_id = ? AND id = ?' => [$courseId, $id]]
         );
@@ -846,7 +846,7 @@ class AnnouncementManager
         }
 
         // store in item_property (first the groups, then the users
-        if (!is_null($to)) {
+        if (!empty($to)) {
             // !is_null($to): when no user is selected we send it to everyone
             $send_to = CourseManager::separateUsersGroups($to);
 
@@ -954,14 +954,14 @@ class AnnouncementManager
      */
     public static function update_mail_sent($insert_id)
     {
-        $tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT);
+        $table = Database::get_course_table(TABLE_ANNOUNCEMENT);
         if ($insert_id != strval(intval($insert_id))) {
             return false;
         }
         $insert_id = intval($insert_id);
         $courseId = api_get_course_int_id();
         // store the modifications in the table tbl_annoucement
-        $sql = "UPDATE $tbl_announcement SET email_sent='1'
+        $sql = "UPDATE $table SET email_sent='1'
                 WHERE c_id = $courseId AND id = $insert_id";
         Database::query($sql);
     }
@@ -1445,7 +1445,7 @@ class AnnouncementManager
     /**
      * @param array $courseInfo
      * @param int   $sessionId
-     * @param int   $id
+     * @param int   $announcementId
      * @param bool  $sendToUsersInSession
      * @param bool  $sendToDrhUsers
      * @param Monolog\Handler\HandlerInterface logger
@@ -1454,13 +1454,13 @@ class AnnouncementManager
     public static function sendEmail(
         $courseInfo,
         $sessionId,
-        $id,
+        $announcementId,
         $sendToUsersInSession = false,
         $sendToDrhUsers = false,
         $logger = null,
         $senderId = 0
     ) {
-        $email = new AnnouncementEmail($courseInfo, $sessionId, $id, $logger);
+        $email = new AnnouncementEmail($courseInfo, $sessionId, $announcementId, $logger);
         $email->send($sendToUsersInSession, $sendToDrhUsers, $senderId);
     }
 
@@ -1505,7 +1505,7 @@ class AnnouncementManager
             $courseId = $courseInfo['real_id'];
         } else {
             $courseId = (int) $courseId;
-            $courseInfo = api_get_course_info($courseId);
+            $courseInfo = api_get_course_info_by_id($courseId);
         }
 
         if (empty($courseInfo)) {

+ 549 - 0
main/inc/lib/CoursesAndSessionsCatalog.class.php

@@ -1,6 +1,10 @@
 <?php
 /* For licensing terms, see /license.txt */
 
+use Chamilo\CoreBundle\Entity\ExtraField;
+use Doctrine\Common\Collections\Criteria;
+use Doctrine\ORM\Query\Expr\Join;
+
 /**
  * @todo change class name
  * Class CoursesAndSessionsCatalog
@@ -63,4 +67,549 @@ class CoursesAndSessionsCatalog
 
         return false;
     }
+
+    /**
+     * @return array
+     */
+    public static function getCoursesToAvoid()
+    {
+        $TABLE_COURSE_FIELD = Database::get_main_table(TABLE_EXTRA_FIELD);
+        $TABLE_COURSE_FIELD_VALUE = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
+
+        // Check special courses
+        $courseListToAvoid = CourseManager::get_special_course_list();
+
+        // Checks "hide_from_catalog" extra field
+        $extraFieldType = ExtraField::COURSE_FIELD_TYPE;
+
+        $sql = "SELECT item_id FROM $TABLE_COURSE_FIELD_VALUE tcfv
+                INNER JOIN $TABLE_COURSE_FIELD tcf
+                ON tcfv.field_id =  tcf.id
+                WHERE
+                    tcf.extra_field_type = $extraFieldType AND
+                    tcf.variable = 'hide_from_catalog' AND
+                    tcfv.value = 1
+                ";
+
+        $result = Database::query($sql);
+        if (Database::num_rows($result) > 0) {
+            while ($row = Database::fetch_array($result)) {
+                $courseListToAvoid[] = $row['item_id'];
+            }
+        }
+
+        return $courseListToAvoid;
+    }
+
+    /**
+     * @return string
+     */
+    public static function getAvoidCourseCondition()
+    {
+        $courseListToAvoid = self::getCoursesToAvoid();
+        $condition = '';
+        if (!empty($courseListToAvoid)) {
+            $courses = [];
+            foreach ($courseListToAvoid as $courseId) {
+                $courses[] = '"'.$courseId.'"';
+            }
+            $condition = ' AND course.id NOT IN ('.implode(',', $courses).')';
+        }
+
+        return $condition;
+    }
+
+    /**
+     * Get available le courses count.
+     *
+     * @param int $accessUrlId (optional)
+     *
+     * @return int Number of courses
+     */
+    public static function countAvailableCoursesToShowInCatalog($accessUrlId = 1)
+    {
+        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
+        $tableCourseRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
+        $courseToAvoidCondition = self::getAvoidCourseCondition();
+
+        $visibilityCondition = CourseManager::getCourseVisibilitySQLCondition('course', true);
+
+        $accessUrlId = (int) $accessUrlId;
+        if (empty($accessUrlId)) {
+            $accessUrlId = 1;
+        }
+
+        $sql = "SELECT count(course.id) 
+                FROM $tableCourse course
+                INNER JOIN $tableCourseRelAccessUrl u
+                ON (course.id = u.c_id)
+                WHERE
+                    u.access_url_id = $accessUrlId AND
+                    course.visibility != 0 AND
+                    course.visibility != 4
+                    $courseToAvoidCondition
+                    $visibilityCondition
+                ";
+
+        $res = Database::query($sql);
+        $row = Database::fetch_row($res);
+
+        return $row[0];
+    }
+
+    /**
+     * @return array
+     */
+    public static function getCourseCategories()
+    {
+        $urlId = 1;
+        if (api_is_multiple_url_enabled()) {
+            $urlId = api_get_current_access_url_id();
+        }
+
+        $countCourses = self::countAvailableCoursesToShowInCatalog($urlId);
+
+        $categories = [];
+        $categories[0][0] = [
+            'id' => 0,
+            'name' => get_lang('DisplayAll'),
+            'code' => 'ALL',
+            'parent_id' => null,
+            'tree_pos' => 0,
+            'count_courses' => $countCourses,
+        ];
+
+        $categoriesFromDatabase = CourseCategory::getCategories();
+        foreach ($categoriesFromDatabase as $row) {
+            $count_courses = CourseCategory::countCoursesInCategory($row['code']);
+            $row['count_courses'] = $count_courses;
+            if (empty($row['parent_id'])) {
+                $categories[0][$row['tree_pos']] = $row;
+            } else {
+                $categories[$row['parent_id']][$row['tree_pos']] = $row;
+            }
+        }
+        $count_courses = CourseCategory::countCoursesInCategory();
+        $categories[0][count($categories[0]) + 1] = [
+            'id' => 0,
+            'name' => get_lang('None'),
+            'code' => 'NONE',
+            'parent_id' => null,
+            'tree_pos' => $row['tree_pos'] + 1,
+            'children_count' => 0,
+            'auth_course_child' => true,
+            'auth_cat_child' => true,
+            'count_courses' => $count_courses,
+        ];
+
+        return $categories;
+    }
+
+    /**
+     * Return LIMIT to filter SQL query.
+     *
+     * @param array $limit
+     *
+     * @return string
+     */
+    public static function getLimitFilterFromArray($limit)
+    {
+        $limitFilter = '';
+        if (!empty($limit) && is_array($limit)) {
+            $limitStart = isset($limit['start']) ? $limit['start'] : 0;
+            $limitLength = isset($limit['length']) ? $limit['length'] : 12;
+            $limitFilter = 'LIMIT '.$limitStart.', '.$limitLength;
+        }
+
+        return $limitFilter;
+    }
+
+    /**
+     * @param string $category_code
+     * @param int    $random_value
+     * @param array  $limit         will be used if $random_value is not set.
+     *                              This array should contains 'start' and 'length' keys
+     *
+     * @return array
+     */
+    public static function getCoursesInCategory($category_code, $random_value = null, $limit = [])
+    {
+        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
+        $avoidCoursesCondition = self::getAvoidCourseCondition();
+        $visibilityCondition = CourseManager::getCourseVisibilitySQLCondition(
+            'course',
+            true
+        );
+
+        if (!empty($random_value)) {
+            $random_value = intval($random_value);
+
+            $sql = "SELECT COUNT(*) FROM $tbl_course";
+            $result = Database::query($sql);
+            list($num_records) = Database::fetch_row($result);
+
+            if (api_is_multiple_url_enabled()) {
+                $url_access_id = api_get_current_access_url_id();
+                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
+
+                $sql = "SELECT COUNT(*) FROM $tbl_course course
+                        INNER JOIN $tbl_url_rel_course as url_rel_course
+                        ON (url_rel_course.c_id = course.id)
+                        WHERE access_url_id = $url_access_id ";
+                $result = Database::query($sql);
+                list($num_records) = Database::fetch_row($result);
+
+                $sql = "SELECT course.id, course.id as real_id 
+                        FROM $tbl_course course
+                        INNER JOIN $tbl_url_rel_course as url_rel_course
+                        ON (url_rel_course.c_id = course.id)
+                        WHERE
+                            access_url_id = $url_access_id AND
+                            RAND()*$num_records< $random_value
+                            $avoidCoursesCondition 
+                            $visibilityCondition
+                        ORDER BY RAND()
+                        LIMIT 0, $random_value";
+            } else {
+                $sql = "SELECT id, id as real_id FROM $tbl_course course
+                        WHERE 
+                            RAND()*$num_records< $random_value 
+                            $avoidCoursesCondition 
+                            $visibilityCondition
+                        ORDER BY RAND()
+                        LIMIT 0, $random_value";
+            }
+
+            $result = Database::query($sql);
+            $id_in = null;
+            while (list($id) = Database::fetch_row($result)) {
+                if ($id_in) {
+                    $id_in .= ",$id";
+                } else {
+                    $id_in = "$id";
+                }
+            }
+            if ($id_in === null) {
+                return [];
+            }
+            $sql = "SELECT *, id as real_id FROM $tbl_course WHERE id IN($id_in)";
+        } else {
+            $limitFilter = self::getLimitFilterFromArray($limit);
+            $category_code = Database::escape_string($category_code);
+            if (empty($category_code) || $category_code == "ALL") {
+                $sql = "SELECT *, id as real_id 
+                        FROM $tbl_course course
+                        WHERE
+                          1=1
+                          $avoidCoursesCondition
+                          $visibilityCondition
+                    ORDER BY title $limitFilter ";
+            } else {
+                if ($category_code == 'NONE') {
+                    $category_code = '';
+                }
+                $sql = "SELECT *, id as real_id FROM $tbl_course course
+                        WHERE
+                            category_code='$category_code'
+                            $avoidCoursesCondition
+                            $visibilityCondition
+                        ORDER BY title $limitFilter ";
+            }
+
+            // Showing only the courses of the current Chamilo access_url_id
+            if (api_is_multiple_url_enabled()) {
+                $url_access_id = api_get_current_access_url_id();
+                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
+                if ($category_code != "ALL") {
+                    $sql = "SELECT *, course.id real_id FROM $tbl_course as course
+                            INNER JOIN $tbl_url_rel_course as url_rel_course
+                            ON (url_rel_course.c_id = course.id)
+                            WHERE
+                                access_url_id = $url_access_id AND
+                                category_code='$category_code'
+                                $avoidCoursesCondition
+                                $visibilityCondition
+                            ORDER BY title $limitFilter";
+                } else {
+                    $sql = "SELECT *, course.id real_id FROM $tbl_course as course
+                            INNER JOIN $tbl_url_rel_course as url_rel_course
+                            ON (url_rel_course.c_id = course.id)
+                            WHERE
+                                access_url_id = $url_access_id
+                                $avoidCoursesCondition
+                                $visibilityCondition
+                            ORDER BY title $limitFilter";
+                }
+            }
+        }
+
+        $result = Database::query($sql);
+        $courses = [];
+        while ($row = Database::fetch_array($result)) {
+            $row['registration_code'] = !empty($row['registration_code']);
+            $count_users = CourseManager::get_users_count_in_course($row['code']);
+            $count_connections_last_month = Tracking::get_course_connections_count(
+                $row['id'],
+                0,
+                api_get_utc_datetime(time() - (30 * 86400))
+            );
+
+            if ($row['tutor_name'] == '0') {
+                $row['tutor_name'] = get_lang('NoManager');
+            }
+            $point_info = CourseManager::get_course_ranking($row['id'], 0);
+            $courses[] = [
+                'real_id' => $row['real_id'],
+                'point_info' => $point_info,
+                'code' => $row['code'],
+                'directory' => $row['directory'],
+                'visual_code' => $row['visual_code'],
+                'title' => $row['title'],
+                'tutor' => $row['tutor_name'],
+                'subscribe' => $row['subscribe'],
+                'unsubscribe' => $row['unsubscribe'],
+                'registration_code' => $row['registration_code'],
+                'creation_date' => $row['creation_date'],
+                'visibility' => $row['visibility'],
+                'category' => $row['category_code'],
+                'count_users' => $count_users,
+                'count_connections' => $count_connections_last_month,
+            ];
+        }
+
+        return $courses;
+    }
+
+    /**
+     * Search the courses database for a course that matches the search term.
+     * The search is done on the code, title and tutor field of the course table.
+     *
+     * @param string $search_term The string that the user submitted, what we are looking for
+     * @param array  $limit
+     * @param bool   $justVisible search only on visible courses in the catalogue
+     *
+     * @return array an array containing a list of all the courses matching the the search term
+     */
+    public static function search_courses($search_term, $limit, $justVisible = false)
+    {
+        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
+        $limitFilter = self::getLimitFilterFromArray($limit);
+        $avoidCoursesCondition = self::getAvoidCourseCondition();
+        $visibilityCondition = $justVisible ? CourseManager::getCourseVisibilitySQLCondition('course', true) : '';
+
+        $search_term_safe = Database::escape_string($search_term);
+        $sql_find = "SELECT * FROM $courseTable course
+                      WHERE (
+                            course.code LIKE '%".$search_term_safe."%' OR
+                            course.title LIKE '%".$search_term_safe."%' OR
+                            course.tutor_name LIKE '%".$search_term_safe."%'
+                        )
+                        $avoidCoursesCondition
+                        $visibilityCondition
+                    ORDER BY title, visual_code ASC
+                    $limitFilter
+                    ";
+
+        if (api_is_multiple_url_enabled()) {
+            $url_access_id = api_get_current_access_url_id();
+            if ($url_access_id != -1) {
+                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
+                $sql_find = "SELECT *
+                            FROM $courseTable as course
+                            INNER JOIN $tbl_url_rel_course as url_rel_course
+                            ON (url_rel_course.c_id = course.id)
+                            WHERE
+                                access_url_id = $url_access_id AND (
+                                    code LIKE '%".$search_term_safe."%' OR
+                                    title LIKE '%".$search_term_safe."%' OR
+                                    tutor_name LIKE '%".$search_term_safe."%'
+                                )
+                                $avoidCoursesCondition
+                                $visibilityCondition
+                            ORDER BY title, visual_code ASC
+                            $limitFilter
+                            ";
+            }
+        }
+        $result_find = Database::query($sql_find);
+        $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(
+                $row['id'],
+                0,
+                api_get_utc_datetime(time() - (30 * 86400))
+            );
+
+            $point_info = CourseManager::get_course_ranking($row['id'], 0);
+
+            $courses[] = [
+                'real_id' => $row['id'],
+                'point_info' => $point_info,
+                'code' => $row['code'],
+                'directory' => $row['directory'],
+                'visual_code' => $row['visual_code'],
+                'title' => $row['title'],
+                'tutor' => $row['tutor_name'],
+                'subscribe' => $row['subscribe'],
+                'unsubscribe' => $row['unsubscribe'],
+                'registration_code' => $row['registration_code'],
+                'creation_date' => $row['creation_date'],
+                'visibility' => $row['visibility'],
+                'count_users' => $count_users,
+                'count_connections' => $count_connections_last_month,
+            ];
+        }
+
+        return $courses;
+    }
+
+    /**
+     * List the sessions.
+     *
+     * @param string $date  (optional) The date of sessions
+     * @param array  $limit
+     *
+     * @return array The session list
+     */
+    public static function browseSessions($date = null, $limit = [])
+    {
+        $em = Database::getManager();
+        $urlId = api_get_current_access_url_id();
+
+        $sql = "SELECT s.id FROM session s ";
+        $sql .= "
+            INNER JOIN access_url_rel_session ars
+            ON s.id = ars.session_id
+        ";
+
+        $sql .= "
+            WHERE s.nbr_courses > 0
+                AND ars.access_url_id = $urlId
+        ";
+
+        if (!is_null($date)) {
+            $sql .= "
+                AND (
+                    ('$date' BETWEEN DATE(s.access_start_date) AND DATE(s.access_end_date))
+                    OR (s.access_end_date IS NULL)
+                    OR (
+                        s.access_start_date IS NULL
+                        AND s.access_end_date IS NOT NULL
+                        AND DATE(s.access_end_date) >= '$date'
+                    )
+                )
+            ";
+        }
+
+        if (!empty($limit)) {
+            $sql .= "LIMIT {$limit['start']}, {$limit['length']} ";
+        }
+
+        $ids = Database::store_result(Database::query($sql));
+        $sessions = [];
+        foreach ($ids as $id) {
+            $sessions[] = $em->find('ChamiloCoreBundle:Session', $id);
+        }
+
+        return $sessions;
+    }
+
+    /**
+     * Search sessions by searched term by session name.
+     *
+     * @param string $queryTerm Term for search
+     * @param array  $limit     Limit info
+     *
+     * @return array The sessions
+     */
+    public static function browseSessionsBySearch($queryTerm, array $limit)
+    {
+        $sessionsToBrowse = [];
+
+        $criteria = Criteria::create()
+            ->where(
+                Criteria::expr()->contains('name', $queryTerm)
+            )
+            ->setFirstResult($limit['start'])
+            ->setMaxResults($limit['length']);
+
+        $sessions = Database::getManager()
+            ->getRepository('ChamiloCoreBundle:Session')
+            ->matching($criteria);
+
+        foreach ($sessions as $session) {
+            if ($session->getNbrCourses() === 0) {
+                continue;
+            }
+
+            $sessionsToBrowse[] = $session;
+        }
+
+        return $sessionsToBrowse;
+    }
+
+    /**
+     * Search sessions by the tags in their courses.
+     *
+     * @param string $termTag Term for search in tags
+     * @param array  $limit   Limit info
+     *
+     * @return array The sessions
+     */
+    public static function browseSessionsByTags($termTag, array $limit)
+    {
+        $em = Database::getManager();
+        $qb = $em->createQueryBuilder();
+
+        $sessions = $qb->select('s')
+            ->distinct(true)
+            ->from('ChamiloCoreBundle:Session', 's')
+            ->innerJoin(
+                'ChamiloCoreBundle:SessionRelCourse',
+                'src',
+                Join::WITH,
+                's.id = src.session'
+            )
+            ->innerJoin(
+                'ChamiloCoreBundle:ExtraFieldRelTag',
+                'frt',
+                Join::WITH,
+                'src.course = frt.itemId'
+            )
+            ->innerJoin(
+                'ChamiloCoreBundle:Tag',
+                't',
+                Join::WITH,
+                'frt.tagId = t.id'
+            )
+            ->innerJoin(
+                'ChamiloCoreBundle:ExtraField',
+                'f',
+                Join::WITH,
+                'frt.fieldId = f.id'
+            )
+            ->where(
+                $qb->expr()->like('t.tag', ":tag")
+            )
+            ->andWhere(
+                $qb->expr()->eq('f.extraFieldType', ExtraField::COURSE_FIELD_TYPE)
+            )
+            ->setFirstResult($limit['start'])
+            ->setMaxResults($limit['length'])
+            ->setParameter('tag', "$termTag%")
+            ->getQuery()
+            ->getResult();
+
+        $sessionsToBrowse = [];
+        foreach ($sessions as $session) {
+            if ($session->getNbrCourses() === 0) {
+                continue;
+            }
+            $sessionsToBrowse[] = $session;
+        }
+
+        return $sessionsToBrowse;
+    }
 }

+ 13 - 7
main/inc/lib/api.lib.php

@@ -165,6 +165,7 @@ define('SECTION_SOCIAL', 'social-network');
 define('SECTION_DASHBOARD', 'dashboard');
 define('SECTION_REPORTS', 'reports');
 define('SECTION_GLOBAL', 'global');
+define('SECTION_INCLUDE', 'include');
 
 // CONSTANT name for local authentication source
 define('PLATFORM_AUTH_SOURCE', 'platform');
@@ -1853,7 +1854,7 @@ function api_get_anonymous_id()
     // Find if another anon is connected now
     $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
     $tableU = Database::get_main_table(TABLE_MAIN_USER);
-    $ip = api_get_real_ip();
+    $ip = Database::escape_string(api_get_real_ip());
     $max = api_get_configuration_value('max_anonymous_users');
     if ($max >= 2) {
         $sql = "SELECT * FROM $table as TEL 
@@ -3379,10 +3380,6 @@ function api_is_allowed_to_edit(
     $session_coach = false,
     $check_student_view = true
 ) {
-    $sessionId = api_get_session_id();
-    $is_allowed_coach_to_edit = api_is_coach(null, null, $check_student_view);
-    $session_visibility = api_get_session_visibility($sessionId);
-
     // Admins can edit anything.
     if (api_is_platform_admin(false)) {
         //The student preview was on
@@ -3393,6 +3390,9 @@ function api_is_allowed_to_edit(
         }
     }
 
+    $sessionId = api_get_session_id();
+    $is_allowed_coach_to_edit = api_is_coach(null, null, $check_student_view);
+    $session_visibility = api_get_session_visibility($sessionId);
     $is_courseAdmin = api_is_course_admin();
 
     if (!$is_courseAdmin && $tutor) {
@@ -5016,9 +5016,9 @@ function api_get_visual_theme()
         }
 
         $course_id = api_get_course_id();
-        if (!empty($course_id) && $course_id != -1) {
+        if (!empty($course_id)) {
             if (api_get_setting('allow_course_theme') == 'true') {
-                $course_theme = api_get_course_setting('course_theme');
+                $course_theme = api_get_course_setting('course_theme', $course_id);
 
                 if (!empty($course_theme) && $course_theme != -1) {
                     if (!empty($course_theme)) {
@@ -6356,9 +6356,13 @@ function api_get_current_access_url_id()
     $result = Database::query($sql);
     if (Database::num_rows($result) > 0) {
         $access_url_id = Database::result($result, 0, 0);
+        if ($access_url_id === false) {
+            return -1;
+        }
 
         return $access_url_id;
     }
+
     //if the url in WEB_PATH was not found, it can only mean that there is
     // either a configuration problem or the first URL has not been defined yet
     // (by default it is http://localhost/). Thus the more sensible thing we can
@@ -7525,6 +7529,8 @@ function api_user_is_login($user_id = null)
  * Guess the real ip for register in the database, even in reverse proxy cases.
  * To be recognized, the IP has to be found in either $_SERVER['REMOTE_ADDR'] or
  * in $_SERVER['HTTP_X_FORWARDED_FOR'], which is in common use with rproxies.
+ * Note: the result of this function is not SQL-safe. Please escape it before
+ * inserting in a database.
  *
  * @return string the user's real ip (unsafe - escape it before inserting to db)
  *

+ 6 - 395
main/inc/lib/auth.lib.php

@@ -35,31 +35,7 @@ class Auth
     {
         $TABLECOURS = Database::get_main_table(TABLE_MAIN_COURSE);
         $TABLECOURSUSER = Database::get_main_table(TABLE_MAIN_COURSE_USER);
-        $TABLE_COURSE_FIELD = Database::get_main_table(TABLE_EXTRA_FIELD);
-        $TABLE_COURSE_FIELD_VALUE = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
-
-        $extraFieldType = ExtraField::COURSE_FIELD_TYPE;
-        // get course list auto-register
-        $sql = "SELECT item_id FROM $TABLE_COURSE_FIELD_VALUE tcfv
-                INNER JOIN $TABLE_COURSE_FIELD tcf
-                ON tcfv.field_id =  tcf.id
-                WHERE
-                    tcf.extra_field_type = $extraFieldType AND
-                    tcf.variable = 'special_course' AND
-                    tcfv.value = 1
-                ";
-
-        $result = Database::query($sql);
-        $special_course_list = [];
-        if (Database::num_rows($result) > 0) {
-            while ($result_row = Database::fetch_array($result)) {
-                $special_course_list[] = '"'.$result_row['item_id'].'"';
-            }
-        }
-        $without_special_courses = '';
-        if (!empty($special_course_list)) {
-            $without_special_courses = ' AND course.id NOT IN ('.implode(',', $special_course_list).')';
-        }
+        $avoidCoursesCondition = CoursesAndSessionsCatalog::getAvoidCourseCondition();
 
         // Secondly we select the courses that are in a category (user_course_cat<>0) and
         // sort these according to the sort of the category
@@ -80,7 +56,8 @@ class Auth
                 WHERE
                     course.id = course_rel_user.c_id AND
                     course_rel_user.relation_type<>".COURSE_RELATION_TYPE_RRHH." AND
-                    course_rel_user.user_id = '".$user_id."' $without_special_courses
+                    course_rel_user.user_id = '".$user_id."' 
+                    $avoidCoursesCondition
                 ORDER BY course_rel_user.sort ASC";
         $result = Database::query($sql);
         $courses = [];
@@ -104,16 +81,6 @@ class Auth
         return $courses;
     }
 
-    /**
-     * retrieves the user defined course categories.
-     *
-     * @return array containing all the IDs of the user defined courses categories, sorted by the "sort" field
-     */
-    public function get_user_course_categories()
-    {
-        return CourseManager::get_user_course_categories(api_get_user_id());
-    }
-
     /**
      * This function get all the courses in the particular user category;.
      *
@@ -126,32 +93,7 @@ class Auth
         // table definitions
         $TABLECOURS = Database::get_main_table(TABLE_MAIN_COURSE);
         $TABLECOURSUSER = Database::get_main_table(TABLE_MAIN_COURSE_USER);
-        $TABLE_COURSE_FIELD = Database::get_main_table(TABLE_EXTRA_FIELD);
-        $TABLE_COURSE_FIELD_VALUE = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
-        $extraFieldType = ExtraField::COURSE_FIELD_TYPE;
-
-        // get course list auto-register
-        $sql = "SELECT item_id
-                FROM $TABLE_COURSE_FIELD_VALUE tcfv
-                INNER JOIN $TABLE_COURSE_FIELD tcf
-                ON tcfv.field_id =  tcf.id
-                WHERE
-                    tcf.extra_field_type = $extraFieldType AND
-                    tcf.variable = 'special_course' AND
-                    tcfv.value = 1 ";
-
-        $result = Database::query($sql);
-        $special_course_list = [];
-        if (Database::num_rows($result) > 0) {
-            while ($result_row = Database::fetch_array($result)) {
-                $special_course_list[] = '"'.$result_row['item_id'].'"';
-            }
-        }
-
-        $without_special_courses = '';
-        if (!empty($special_course_list)) {
-            $without_special_courses = ' AND course.id NOT IN ('.implode(',', $special_course_list).')';
-        }
+        $avoidCoursesCondition = CoursesAndSessionsCatalog::getAvoidCourseCondition();
 
         $sql = "SELECT
                     course.code, course.visual_code, course.subscribe subscr, course.unsubscribe unsubscr,
@@ -163,7 +105,7 @@ class Auth
                     course.id = course_rel_user.c_id AND
                     course_rel_user.user_id = '".$user_id."' AND
                     course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
-                    $without_special_courses
+                    $avoidCoursesCondition
                 ORDER BY course_rel_user.user_course_cat, course_rel_user.sort ASC";
         $result = Database::query($sql);
         $data = [];
@@ -292,7 +234,7 @@ class Auth
     public function move_category($direction, $category2move)
     {
         $userId = api_get_user_id();
-        $userCategories = $this->get_user_course_categories();
+        $userCategories = CourseManager::get_user_course_categories(api_get_user_id());
         $categories = array_values($userCategories);
 
         $previous = null;
@@ -393,114 +335,6 @@ class Auth
         return $result;
     }
 
-    /**
-     * Search the courses database for a course that matches the search term.
-     * The search is done on the code, title and tutor field of the course table.
-     *
-     * @param string $search_term The string that the user submitted, what we are looking for
-     * @param array  $limit
-     * @param bool   $justVisible search only on visible courses in the catalogue
-     *
-     * @return array an array containing a list of all the courses matching the the search term
-     */
-    public function search_courses($search_term, $limit, $justVisible = false)
-    {
-        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
-        $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD);
-        $extraFieldValuesTable = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
-
-        $limitFilter = CourseCategory::getLimitFilterFromArray($limit);
-
-        // get course list auto-register
-        $sql = "SELECT item_id
-                FROM $extraFieldValuesTable tcfv
-                INNER JOIN $extraFieldTable tcf ON tcfv.field_id =  tcf.id
-                WHERE
-                    tcf.variable = 'special_course' AND
-                    tcfv.value = 1 ";
-
-        $special_course_result = Database::query($sql);
-        if (Database::num_rows($special_course_result) > 0) {
-            $special_course_list = [];
-            while ($result_row = Database::fetch_array($special_course_result)) {
-                $special_course_list[] = '"'.$result_row['item_id'].'"';
-            }
-        }
-        $without_special_courses = '';
-        if (!empty($special_course_list)) {
-            $without_special_courses = ' AND course.code NOT IN ('.implode(',', $special_course_list).')';
-        }
-
-        $visibilityCondition = $justVisible ? CourseManager::getCourseVisibilitySQLCondition('course', true) : '';
-
-        $search_term_safe = Database::escape_string($search_term);
-        $sql_find = "SELECT * FROM $courseTable
-                    WHERE (
-                            code LIKE '%".$search_term_safe."%' OR
-                            title LIKE '%".$search_term_safe."%' OR
-                            tutor_name LIKE '%".$search_term_safe."%'
-                        )
-                        $without_special_courses
-                        $visibilityCondition
-                    ORDER BY title, visual_code ASC
-                    $limitFilter
-                    ";
-
-        if (api_is_multiple_url_enabled()) {
-            $url_access_id = api_get_current_access_url_id();
-            if ($url_access_id != -1) {
-                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
-                $sql_find = "SELECT *
-                            FROM $courseTable as course
-                            INNER JOIN $tbl_url_rel_course as url_rel_course
-                            ON (url_rel_course.c_id = course.id)
-                            WHERE
-                                access_url_id = $url_access_id AND (
-                                    code LIKE '%".$search_term_safe."%' OR
-                                    title LIKE '%".$search_term_safe."%' OR
-                                    tutor_name LIKE '%".$search_term_safe."%'
-                                )
-                                $without_special_courses
-                                $visibilityCondition
-                            ORDER BY title, visual_code ASC
-                            $limitFilter
-                            ";
-            }
-        }
-        $result_find = Database::query($sql_find);
-        $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(
-                $row['id'],
-                0,
-                api_get_utc_datetime(time() - (30 * 86400))
-            );
-
-            $point_info = CourseManager::get_course_ranking($row['id'], 0);
-
-            $courses[] = [
-                'real_id' => $row['id'],
-                'point_info' => $point_info,
-                'code' => $row['code'],
-                'directory' => $row['directory'],
-                'visual_code' => $row['visual_code'],
-                'title' => $row['title'],
-                'tutor' => $row['tutor_name'],
-                'subscribe' => $row['subscribe'],
-                'unsubscribe' => $row['unsubscribe'],
-                'registration_code' => $row['registration_code'],
-                'creation_date' => $row['creation_date'],
-                'visibility' => $row['visibility'],
-                'count_users' => $count_users,
-                'count_connections' => $count_connections_last_month,
-            ];
-        }
-
-        return $courses;
-    }
-
     /**
      * unsubscribe the user from a given course.
      *
@@ -585,44 +419,6 @@ class Auth
         return $result;
     }
 
-    /**
-     * Counts the number of courses in a given course category.
-     *
-     * @param string $categoryCode Category code
-     * @param $searchTerm
-     *
-     * @return int Count of courses
-     */
-    public function count_courses_in_category($categoryCode, $searchTerm = '')
-    {
-        return CourseCategory::countCoursesInCategory($categoryCode, $searchTerm);
-    }
-
-    /**
-     * get the browsing of the course categories (faculties).
-     *
-     * @return array array containing a list with all the categories and subcategories(if needed)
-     */
-    public function browse_course_categories()
-    {
-        return CourseCategory::browseCourseCategories();
-    }
-
-    /**
-     * Display all the courses in the given course category. I could have used a parameter here.
-     *
-     * @param string $categoryCode Category code
-     * @param int    $randomValue
-     * @param array  $limit        will be used if $random_value is not set.
-     *                             This array should contains 'start' and 'length' keys
-     *
-     * @return array Courses data
-     */
-    public function browse_courses_in_category($categoryCode, $randomValue = null, $limit = [])
-    {
-        return CourseCategory::browseCoursesInCategory($categoryCode, $randomValue, $limit);
-    }
-
     /**
      * Subscribe the user to a given course.
      *
@@ -692,189 +488,4 @@ class Auth
             return ['message' => $message, 'content' => $content];
         }
     }
-
-    /**
-     * List the sessions.
-     *
-     * @param string $date  (optional) The date of sessions
-     * @param array  $limit
-     *
-     * @return array The session list
-     */
-    public function browseSessions($date = null, $limit = [])
-    {
-        $em = Database::getManager();
-        $qb = $em->createQueryBuilder();
-        $urlId = api_get_current_access_url_id();
-
-        $sql = "SELECT s.id FROM session s ";
-        $sql .= "
-            INNER JOIN access_url_rel_session ars
-            ON s.id = ars.session_id
-        ";
-
-        $sql .= "
-            WHERE s.nbr_courses > 0
-                AND ars.access_url_id = $urlId
-        ";
-
-        if (!is_null($date)) {
-            $sql .= "
-                AND (
-                    ('$date' BETWEEN DATE(s.access_start_date) AND DATE(s.access_end_date))
-                    OR (s.access_end_date IS NULL)
-                    OR (
-                        s.access_start_date IS NULL
-                        AND s.access_end_date IS NOT NULL
-                        AND DATE(s.access_end_date) >= '$date'
-                    )
-                )
-            ";
-        }
-
-        if (!empty($limit)) {
-            $sql .= "LIMIT {$limit['start']}, {$limit['length']} ";
-        }
-
-        $ids = Database::store_result(Database::query($sql));
-        $sessions = [];
-        foreach ($ids as $id) {
-            $sessions[] = $em->find('ChamiloCoreBundle:Session', $id);
-        }
-
-        return $sessions;
-    }
-
-    /**
-     * Return a COUNT from Session table.
-     *
-     * @param string $date in Y-m-d format
-     *
-     * @return int
-     */
-    public function countSessions($date = null)
-    {
-        $count = 0;
-        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
-        $url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
-        $date = Database::escape_string($date);
-        $urlId = api_get_current_access_url_id();
-        $dateFilter = '';
-        if (!empty($date)) {
-            $dateFilter = <<<SQL
-                AND ('$date' BETWEEN s.access_start_date AND s.access_end_date)
-                OR (s.access_end_date IS NULL)
-                OR (s.access_start_date IS NULL AND
-                s.access_end_date IS NOT NULL AND s.access_end_date > '$date')
-SQL;
-        }
-        $sql = "SELECT COUNT(*) 
-                FROM $sessionTable s
-                INNER JOIN $url u
-                ON (s.id = u.session_id)
-                WHERE u.access_url_id = $urlId $dateFilter";
-        $res = Database::query($sql);
-        if ($res !== false && Database::num_rows($res) > 0) {
-            $count = current(Database::fetch_row($res));
-        }
-
-        return $count;
-    }
-
-    /**
-     * Search sessions by the tags in their courses.
-     *
-     * @param string $termTag Term for search in tags
-     * @param array  $limit   Limit info
-     *
-     * @return array The sessions
-     */
-    public function browseSessionsByTags($termTag, array $limit)
-    {
-        $em = Database::getManager();
-        $qb = $em->createQueryBuilder();
-
-        $sessions = $qb->select('s')
-            ->distinct(true)
-            ->from('ChamiloCoreBundle:Session', 's')
-            ->innerJoin(
-                'ChamiloCoreBundle:SessionRelCourse',
-                'src',
-                Join::WITH,
-                's.id = src.session'
-            )
-            ->innerJoin(
-                'ChamiloCoreBundle:ExtraFieldRelTag',
-                'frt',
-                Join::WITH,
-                'src.course = frt.itemId'
-            )
-            ->innerJoin(
-                'ChamiloCoreBundle:Tag',
-                't',
-                Join::WITH,
-                'frt.tagId = t.id'
-            )
-            ->innerJoin(
-                'ChamiloCoreBundle:ExtraField',
-                'f',
-                Join::WITH,
-                'frt.fieldId = f.id'
-            )
-            ->where(
-                $qb->expr()->like('t.tag', ":tag")
-            )
-            ->andWhere(
-                $qb->expr()->eq('f.extraFieldType', ExtraField::COURSE_FIELD_TYPE)
-            )
-            ->setFirstResult($limit['start'])
-            ->setMaxResults($limit['length'])
-            ->setParameter('tag', "$termTag%")
-            ->getQuery()
-            ->getResult();
-
-        $sessionsToBrowse = [];
-        foreach ($sessions as $session) {
-            if ($session->getNbrCourses() === 0) {
-                continue;
-            }
-            $sessionsToBrowse[] = $session;
-        }
-
-        return $sessionsToBrowse;
-    }
-
-    /**
-     * Search sessions by searched term by session name.
-     *
-     * @param string $queryTerm Term for search
-     * @param array  $limit     Limit info
-     *
-     * @return array The sessions
-     */
-    public function browseSessionsBySearch($queryTerm, array $limit)
-    {
-        $sessionsToBrowse = [];
-
-        $criteria = Criteria::create()
-            ->where(
-                Criteria::expr()->contains('name', $queryTerm)
-            )
-            ->setFirstResult($limit['start'])
-            ->setMaxResults($limit['length']);
-
-        $sessions = Database::getManager()
-                ->getRepository('ChamiloCoreBundle:Session')
-                ->matching($criteria);
-
-        foreach ($sessions as $session) {
-            if ($session->getNbrCourses() === 0) {
-                continue;
-            }
-
-            $sessionsToBrowse[] = $session;
-        }
-
-        return $sessionsToBrowse;
-    }
 }

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

@@ -229,7 +229,7 @@ function returnNotificationMenu()
             (api_get_setting('showonline', 'world') == 'true' && !$user_id) ||
             (api_get_setting('showonline', 'users') == 'true' && $user_id)
         ) {
-            $html .= '<li><a href="'.api_get_path(WEB_PATH).'whoisonline.php" target="_self" title="'
+            $html .= '<li class="user-online"><a href="'.api_get_path(WEB_PATH).'whoisonline.php" target="_self" title="'
                 .get_lang('UsersOnline').'" >'
                 .Display::return_icon('user.png', get_lang('UsersOnline'), [], ICON_SIZE_TINY)
                 .' '.$number.'</a></li>';
@@ -242,7 +242,7 @@ function returnNotificationMenu()
                 api_get_setting('showonline', 'course') == 'true' && isset($courseInfo['sysCode'])
             )
         ) {
-            $html .= '<li><a href="'.api_get_path(WEB_PATH).'whoisonline.php?cidReq='.$courseInfo['sysCode']
+            $html .= '<li class="user-online-course"><a href="'.api_get_path(WEB_PATH).'whoisonline.php?cidReq='.$courseInfo['sysCode']
                 .'" target="_self">'
                 .Display::return_icon('course.png', get_lang('UsersOnline').' '.get_lang('InThisCourse'), [], ICON_SIZE_TINY)
                 .' '.$number_online_in_course.' </a></li>';
@@ -251,7 +251,7 @@ function returnNotificationMenu()
         if (isset($user_id) && $sessionId != 0) {
             $numberOnlineInSession = getOnlineUsersInSessionCount($sessionId);
 
-            $html .= '<li><a href="'.api_get_path(WEB_PATH).'whoisonlinesession.php?id_coach='.$user_id.'" target="_self">'
+            $html .= '<li class="user-online-session"><a href="'.api_get_path(WEB_PATH).'whoisonlinesession.php?id_coach='.$user_id.'" target="_self">'
                 .Display::return_icon('session.png', get_lang('UsersConnectedToMySessions'), [], ICON_SIZE_TINY)
                 .' '.$numberOnlineInSession
                 .'</a></li>';

+ 18 - 17
main/inc/lib/blog.lib.php

@@ -28,10 +28,10 @@ class Blog
         $course_id = api_get_course_int_id();
 
         if (is_numeric($blog_id)) {
-            $tbl_blogs = Database::get_course_table(TABLE_BLOGS);
+            $table = Database::get_course_table(TABLE_BLOGS);
 
             $sql = "SELECT blog_name
-                    FROM $tbl_blogs
+                    FROM $table
                     WHERE c_id = $course_id AND blog_id = ".intval($blog_id);
 
             $result = Database::query($sql);
@@ -52,9 +52,9 @@ class Blog
      */
     public static function getBlogSubtitle($blog_id)
     {
-        $tbl_blogs = Database::get_course_table(TABLE_BLOGS);
+        $table = Database::get_course_table(TABLE_BLOGS);
         $course_id = api_get_course_int_id();
-        $sql = "SELECT blog_subtitle FROM $tbl_blogs
+        $sql = "SELECT blog_subtitle FROM $table
                 WHERE c_id = $course_id AND blog_id ='".intval($blog_id)."'";
         $result = Database::query($sql);
         $blog = Database::fetch_array($result);
@@ -446,7 +446,7 @@ class Blog
      */
     public static function editPost($post_id, $title, $full_text, $blog_id)
     {
-        $tbl_blogs_posts = Database::get_course_table(TABLE_BLOGS_POSTS);
+        $table = Database::get_course_table(TABLE_BLOGS_POSTS);
         $course_id = api_get_course_int_id();
         $title = Database::escape_string($title);
         $full_text = Database::escape_string($full_text);
@@ -454,7 +454,7 @@ class Blog
         $blog_id = intval($blog_id);
 
         // Create the post
-        $sql = "UPDATE $tbl_blogs_posts SET
+        $sql = "UPDATE $table SET
                 title = '$title',
                 full_text = '$full_text'
                 WHERE c_id = $course_id AND post_id = $post_id AND blog_id = $blog_id
@@ -821,13 +821,13 @@ class Blog
      */
     public static function deleteTask($blog_id, $task_id)
     {
-        $tbl_blogs_tasks = Database::get_course_table(TABLE_BLOGS_TASKS);
+        $table = Database::get_course_table(TABLE_BLOGS_TASKS);
         $course_id = api_get_course_int_id();
         $blog_id = intval($blog_id);
         $task_id = intval($task_id);
 
         // Delete posts
-        $sql = "DELETE FROM $tbl_blogs_tasks
+        $sql = "DELETE FROM $table
                 WHERE c_id = $course_id AND blog_id = $blog_id AND task_id = $task_id";
         Database::query($sql);
     }
@@ -1017,6 +1017,7 @@ class Blog
         // Display
         if (Database::num_rows($result) > 0) {
             $limit = 200;
+            $listArticle = [];
             while ($blog_post = Database::fetch_array($result)) {
                 // Get number of comments
                 $sql = "SELECT COUNT(1) as number_of_comments
@@ -1408,14 +1409,14 @@ class Blog
      */
     public static function displayRating($type, $blog_id, $item_id)
     {
-        $tbl_blogs_rating = Database::get_course_table(TABLE_BLOGS_RATING);
+        $table = Database::get_course_table(TABLE_BLOGS_RATING);
         $course_id = api_get_course_int_id();
         $blog_id = intval($blog_id);
         $item_id = intval($item_id);
         $type = Database::escape_string($type);
 
         // Calculate rating
-        $sql = "SELECT AVG(rating) as rating FROM $tbl_blogs_rating
+        $sql = "SELECT AVG(rating) as rating FROM $table
                 WHERE
                     c_id = $course_id AND
                     blog_id = $blog_id AND
@@ -2187,7 +2188,7 @@ class Blog
      */
     public static function displayAssignedTaskEditForm($blog_id, $task_id, $user_id)
     {
-        $tbl_blogs_tasks_rel_user = Database::get_course_table(TABLE_BLOGS_TASKS_REL_USER);
+        $table = Database::get_course_table(TABLE_BLOGS_TASKS_REL_USER);
 
         $course_id = api_get_course_int_id();
         $blog_id = intval($blog_id);
@@ -2197,7 +2198,7 @@ class Blog
         // Get assign date;
         $sql = "
             SELECT target_date
-            FROM $tbl_blogs_tasks_rel_user
+            FROM $table
             WHERE c_id = $course_id AND
                   blog_id = $blog_id AND
                   user_id = $user_id AND
@@ -2232,7 +2233,7 @@ class Blog
      */
     public static function assignTask($blog_id, $user_id, $task_id, $target_date)
     {
-        $tbl_blogs_tasks_rel_user = Database::get_course_table(TABLE_BLOGS_TASKS_REL_USER);
+        $table = Database::get_course_table(TABLE_BLOGS_TASKS_REL_USER);
         $course_id = api_get_course_int_id();
         $blog_id = intval($blog_id);
         $user_id = intval($user_id);
@@ -2241,7 +2242,7 @@ class Blog
 
         $sql = "
             SELECT COUNT(*) as 'number'
-            FROM $tbl_blogs_tasks_rel_user
+            FROM $table
             WHERE c_id = $course_id 
             AND blog_id = $blog_id 
             AND	user_id = $user_id 
@@ -2252,7 +2253,7 @@ class Blog
 
         if ($row['number'] == 0) {
             $sql = "
-                INSERT INTO ".$tbl_blogs_tasks_rel_user." (
+                INSERT INTO ".$table." (
                     c_id,
                     blog_id,
                     user_id,
@@ -2579,7 +2580,8 @@ class Blog
         $course_id = api_get_course_int_id();
 
         $sql = "SELECT user.user_id, user.lastname, user.firstname, user.email, user.username
-                FROM $tbl_users user INNER JOIN $tbl_blogs_rel_user blogs_rel_user
+                FROM $tbl_users user 
+                INNER JOIN $tbl_blogs_rel_user blogs_rel_user
                 ON user.user_id = blogs_rel_user.user_id
                 WHERE blogs_rel_user.c_id = $course_id AND  blogs_rel_user.blog_id = $blog_id";
 
@@ -2588,7 +2590,6 @@ class Blog
         }
 
         $user_data = [];
-
         while ($myrow = Database::fetch_array($sql_result)) {
             $row = [];
             $row[] = '<input type="checkbox" name="user[]" value="'.$myrow['user_id'].'" '.((isset($_GET['selectall']) && $_GET['selectall'] == "unsubscribe") ? ' checked="checked" ' : '').'/>';

+ 4 - 23
main/inc/lib/career.lib.php

@@ -501,7 +501,6 @@ class Career extends Model
             $list[$column]['rows'][$row]['group_label'] = $groupLabel;
             $list[$column]['rows'][$row]['subgroup'] = $subGroup;
             $list[$column]['rows'][$row]['subgroup_label'] = $subGroupLabel;
-            //$list[$column]['label'] = $subGroupLabel;
             $list[$column]['label'] = $groupLabel;
             $list[$column]['column'] = $column;
         }
@@ -588,7 +587,7 @@ class Career extends Model
             }
         }
 
-        $graphHtml = '<div id ="career_grid">';
+        $graphHtml = '';
         $groupsBetweenColumns = [];
         foreach ($list as $column => $columnList) {
             foreach ($columnList['rows'] as $subGroupList) {
@@ -614,36 +613,18 @@ class Career extends Model
             self::parseColumnList($groupCourseList, $items, '', $graph, $simpleConnectionList);
         }
 
-        $graphHtml .= '</div>';
         $graphHtml .= '<style>
-             #career_grid {
-                 display: grid;
-                 grid-gap: 40px;
-                 grid-template-columns: repeat(6, [col] auto ) ;
-                 grid-template-rows: repeat(2, [row] auto);
-                 background-color: #fff;
-                 color: #444;
-                 justify-items: stretch;
-                 align-items: start;
-                 align-content: start;
-                 justify-content: stretch;
-             }
-             .group_class {
-                 border: solid 2px;
-                 padding: 8px;
-             }
              .panel-title {
                 font-size: 11px;
              }
-             </style>
-             ';
+             </style>';
 
         // Create groups
         if (!empty($graph->groupList)) {
             $groupList = [];
             $groupDiffX = 20;
             $groupDiffY = 10;
-            $style = 'whiteSpace=wrap;rounded;strokeColor=red;fillColor=none;strokeWidth=2;align=left;verticalAlign=top;';
+            $style = 'whiteSpace=wrap;rounded;html=1;strokeColor=red;fillColor=none;strokeWidth=2;align=left;verticalAlign=top;';
             foreach ($graph->groupList as $id => $data) {
                 if (empty($id)) {
                     continue;
@@ -707,6 +688,7 @@ class Career extends Model
                 $subGroupList[] = $vertexData;
             }
         }
+
         // Create connections (arrows)
         if (!empty($simpleConnectionList)) {
             $connectionList = [];
@@ -896,7 +878,6 @@ class Career extends Model
             $style = 'text;html=1;strokeColor=green;fillColor=#ffffff;overflow=fill;rounded=0;align=left;';
 
             $panel = str_replace(["\n", "\r"], '', $panel);
-            //$panel = $title.' - '.$group.' row: '.$originalRow.' heigh: '.$height.' id: '.$id;
             $vertexData = "var v$id = graph.insertVertex(parent, null, '".addslashes($panel)."', $x, $y, $width, $height, '$style');";
 
             $graph->elementList[$id] = $vertexData;

+ 171 - 191
main/inc/lib/course.lib.php

@@ -180,7 +180,7 @@ class CourseManager
         return Database::fetch_array(
             Database::query(
                 "SELECT *, id as real_id FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
-                WHERE code='".Database::escape_string($course_code)."'"
+                WHERE code = '".Database::escape_string($course_code)."'"
             ),
             'ASSOC'
         );
@@ -237,7 +237,7 @@ class CourseManager
 
         if (!empty($urlId)) {
             $urlId = intval($urlId);
-            $sql .= " AND access_url_id= $urlId";
+            $sql .= " AND access_url_id = $urlId";
         }
 
         $allowedFields = [
@@ -339,7 +339,7 @@ class CourseManager
     {
         $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
                 WHERE
-                    c_id  = '".intval($courseId)."' AND
+                    c_id  = ".intval($courseId)." AND
                     user_id = ".intval($userId);
         $result = Database::fetch_array(Database::query($sql));
 
@@ -362,8 +362,8 @@ class CourseManager
 
         $sql = "UPDATE $table SET is_tutor = '".$isTutor."'
 			    WHERE
-				    user_id = '".$userId."' AND
-				    c_id = '".$courseId."'";
+				    user_id = ".$userId." AND
+				    c_id = ".$courseId;
 
         $result = Database::query($sql);
 
@@ -380,16 +380,17 @@ class CourseManager
      *
      * @return mixed
      */
-    public static function get_tutor_in_course_status($user_id, $courseId)
+    public static function get_tutor_in_course_status($userId, $courseId)
     {
+        $userId = intval($userId);
+        $courseId = intval($courseId);
         $result = Database::fetch_array(
             Database::query(
-                "
-                SELECT is_tutor
+                "SELECT is_tutor
                 FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
                 WHERE
-                    c_id = '".Database::escape_string($courseId)."' AND
-                    user_id = ".intval($user_id)
+                    c_id = $courseId AND
+                    user_id = $userId"
             )
         );
 
@@ -473,20 +474,20 @@ class CourseManager
 
         // Unsubscribe user from all blogs in the course.
         $sql = "DELETE FROM ".Database::get_course_table(TABLE_BLOGS_REL_USER)." 
-                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
+                WHERE c_id = $course_id AND user_id IN ($user_ids)";
         Database::query($sql);
 
         $sql = "DELETE FROM ".Database::get_course_table(TABLE_BLOGS_TASKS_REL_USER)." 
-                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
+                WHERE c_id = $course_id AND user_id IN ($user_ids)";
         Database::query($sql);
 
         // Deleting users in forum_notification and mailqueue course tables
         $sql = "DELETE FROM  ".Database::get_course_table(TABLE_FORUM_NOTIFICATION)."
-                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
+                WHERE c_id = $course_id AND user_id IN ($user_ids)";
         Database::query($sql);
 
         $sql = "DELETE FROM ".Database::get_course_table(TABLE_FORUM_MAIL_QUEUE)."
-                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
+                WHERE c_id = $course_id AND user_id IN ($user_ids)";
         Database::query($sql);
 
         // Unsubscribe user from the course.
@@ -494,24 +495,24 @@ class CourseManager
             // Delete in table session_rel_course_rel_user
             $sql = "DELETE FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
                     WHERE
-                        session_id ='".$session_id."' AND
-                        c_id = '".$course_id."' AND
+                        session_id = $session_id AND
+                        c_id = $course_id AND
                         user_id IN ($user_ids)";
             Database::query($sql);
 
             foreach ($userList as $uid) {
                 // check if a user is register in the session with other course
                 $sql = "SELECT user_id FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
-                        WHERE session_id='$session_id' AND user_id='$uid'";
+                        WHERE session_id = $session_id AND user_id = $uid";
                 $rs = Database::query($sql);
 
                 if (Database::num_rows($rs) == 0) {
                     // Delete in table session_rel_user
                     $sql = "DELETE FROM ".Database::get_main_table(TABLE_MAIN_SESSION_USER)."
                             WHERE
-                                session_id ='".$session_id."' AND
-                                user_id = '$uid' AND
-                                relation_type<>".SESSION_RELATION_TYPE_RRHH."";
+                                session_id = $session_id AND
+                                user_id = $uid AND
+                                relation_type<>".SESSION_RELATION_TYPE_RRHH;
                     Database::query($sql);
                 }
             }
@@ -522,20 +523,20 @@ class CourseManager
             $row = Database::fetch_array(Database::query($sql));
             $count = $row[0];
             // number of users by session
-            $sql = "UPDATE ".Database::get_main_table(TABLE_MAIN_SESSION)." SET nbr_users = '$count'
-                    WHERE id = '".$session_id."'";
+            $sql = "UPDATE ".Database::get_main_table(TABLE_MAIN_SESSION)." SET nbr_users = $count
+                    WHERE id = $session_id";
             Database::query($sql);
 
             // Update the table session_rel_course
             $sql = "SELECT COUNT(*) FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
-                    WHERE session_id = '$session_id' AND c_id = '$course_id' AND status<>2";
+                    WHERE session_id = $session_id AND c_id = $course_id AND status <> 2";
             $row = Database::fetch_array(@Database::query($sql));
             $count = $row[0];
 
             // number of users by session and course
             $sql = "UPDATE ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE)."
-                    SET nbr_users = '$count'
-                    WHERE session_id = '$session_id' AND c_id = '$course_id'";
+                    SET nbr_users = $count
+                    WHERE session_id = $session_id AND c_id = $course_id";
             Database::query($sql);
 
             Event::addEvent(
@@ -550,9 +551,9 @@ class CourseManager
         } else {
             $sql = "DELETE FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
                     WHERE
-                        user_id IN (".$user_ids.") AND
+                        user_id IN ($user_ids) AND
                         relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
-                        c_id = '".$course_id."'";
+                        c_id = $course_id";
             Database::query($sql);
 
             // add event to system log
@@ -627,7 +628,7 @@ class CourseManager
 
         // A preliminary check whether the user has bben already registered on the platform.
         $sql = "SELECT status FROM ".Database::get_main_table(TABLE_MAIN_USER)."
-                WHERE user_id = '$user_id' ";
+                WHERE user_id = $user_id";
         if (Database::num_rows(Database::query($sql)) == 0) {
             return false; // The user has not been registered to the platform.
         }
@@ -635,7 +636,7 @@ class CourseManager
         // Check whether the user has not been already subscribed to the course.
         $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."                    
                 WHERE 
-                    user_id = '$user_id' AND 
+                    user_id = $user_id AND 
                     relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND 
                     c_id = $courseId
                 ";
@@ -755,7 +756,7 @@ class CourseManager
     {
         $table = Database::get_main_table(TABLE_MAIN_COURSE);
         $id = intval($id);
-        $sql = "SELECT code FROM $table WHERE id = '$id' ";
+        $sql = "SELECT code FROM $table WHERE id = $id ";
         $res = Database::query($sql);
         $row = Database::fetch_object($res);
         if ($row) {
@@ -801,7 +802,7 @@ class CourseManager
         $courseId = $courseInfo['real_id'];
 
         // Check in advance whether the user has already been registered on the platform.
-        $sql = "SELECT status FROM ".$user_table." WHERE user_id = $user_id ";
+        $sql = "SELECT status FROM $user_table WHERE user_id = $user_id ";
         if (Database::num_rows(Database::query($sql)) == 0) {
             if ($debug) {
                 error_log('The user has not been registered to the platform');
@@ -945,7 +946,7 @@ class CourseManager
         $sql = "SELECT * FROM $courseUserTable
                 WHERE
                     user_id = $userId AND
-                    visible = ".$visible." AND
+                    visible = $visible AND
                     c_id = $courseId";
         if (Database::num_rows(Database::query($sql)) > 0) {
             $cond = [
@@ -1008,8 +1009,8 @@ class CourseManager
                 INNER JOIN $tbl_course as course
                 ON course.id = course_rel_user.c_id
                 WHERE
-                    course_rel_user.user_id='$user_id' AND
-                    course_rel_user.status='1'
+                    course_rel_user.user_id = $user_id AND
+                    course_rel_user.status = 1
         ";
 
         if (api_get_multiple_access_url()) {
@@ -1029,8 +1030,8 @@ class CourseManager
                     ON (course_rel_url.c_id = course.id)
                     WHERE
                         access_url_id = $access_url_id  AND
-                        course_rel_user.user_id = '$user_id' AND
-                        course_rel_user.status = '1'
+                        course_rel_user.user_id = $user_id AND
+                        course_rel_user.status = 1
                 ";
             }
         }
@@ -1146,7 +1147,7 @@ class CourseManager
             return true;
         }
 
-        $sql = 'SELECT 1 FROM '.$tableSessionCourseUser.' WHERE user_id = '.$user_id.' AND status=2 '.$condition_course;
+        $sql = 'SELECT 1 FROM '.$tableSessionCourseUser.' WHERE user_id = '.$user_id.' AND status = 2 '.$condition_course;
         if (Database::num_rows(Database::query($sql)) > 0) {
             return true;
         }
@@ -1218,9 +1219,9 @@ class CourseManager
                     LEFT JOIN ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." course_user
                     ON course.id = course_user.c_id
                     WHERE
-                        course_user.user_id = '$user_id' AND
+                        course_user.user_id = $user_id AND
                         course_user.relation_type<>".COURSE_RELATION_TYPE_RRHH." AND
-                        ( course.id = '$courseId')"
+                        ( course.id = $courseId)"
                 )
             );
 
@@ -1232,8 +1233,8 @@ class CourseManager
         // A user?
         if (Database::num_rows(Database::query("SELECT user_id
                 FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
-                WHERE session_id='".$session_id."'
-                AND user_id ='$user_id'"))
+                WHERE session_id = $session_id
+                AND user_id = $user_id"))
         ) {
             return true;
         }
@@ -1241,9 +1242,9 @@ class CourseManager
         // A course coach?
         if (Database::num_rows(Database::query("SELECT user_id
                 FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
-                WHERE session_id='".$session_id."'
-                AND user_id = '$user_id' AND status = 2
-                AND c_id = '$courseId'"))
+                WHERE session_id = $session_id
+                AND user_id = $user_id AND status = 2
+                AND c_id = $courseId"))
         ) {
             return true;
         }
@@ -1251,8 +1252,8 @@ class CourseManager
         // A session coach?
         if (Database::num_rows(Database::query("SELECT id_coach
                 FROM ".Database::get_main_table(TABLE_MAIN_SESSION)." AS session
-                WHERE session.id='".$session_id."'
-                AND id_coach = '$user_id'"))
+                WHERE session.id = $session_id
+                AND id_coach = $user_id"))
         ) {
             return true;
         }
@@ -1764,7 +1765,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']);
@@ -1776,12 +1777,14 @@ class CourseManager
         if ($addGeneralCoach) {
             $table = Database::get_main_table(TABLE_MAIN_SESSION);
             // We get the session coach.
-            $sql = 'SELECT id_coach FROM '.$table.' WHERE id='.$session_id;
+            $sql = "SELECT id_coach FROM $table WHERE id = $session_id";
             $rs = Database::query($sql);
             $session_id_coach = Database::result($rs, 0, 'id_coach');
-            $userInfo = api_get_user_info($session_id_coach);
-            if ($userInfo) {
-                $users[$session_id_coach] = $userInfo;
+            if (is_int($session_id_coach)) {
+                $userInfo = api_get_user_info($session_id_coach);
+                if ($userInfo) {
+                    $users[$session_id_coach] = $userInfo;
+                }
             }
         }
 
@@ -1859,7 +1862,7 @@ class CourseManager
                           FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)." scu
                           $joinSession
                           INNER JOIN $userTable u ON scu.user_id = u.user_id
-                          WHERE scu.c_id = '$courseId' AND scu.status <> 2";
+                          WHERE scu.c_id = $courseId AND scu.status <> 2";
 
             if (!empty($date_from) && !empty($date_to)) {
                 $date_from = Database::escape_string($date_from);
@@ -2068,8 +2071,8 @@ class CourseManager
                 INNER JOIN $tbl_session_course_user scu
                 ON (u.user_id = scu.user_id)
                 WHERE
-                    scu.session_id = '$session_id' AND
-                    scu.c_id = '$courseId' AND
+                    scu.session_id = $session_id AND
+                    scu.c_id = $courseId AND
                     scu.status = 2";
         $rs = Database::query($sql);
 
@@ -2246,10 +2249,10 @@ class CourseManager
         }
 
         $sql = "SELECT * FROM $table_course
-                WHERE code = '".$codeFiltered."'";
+                WHERE code = '$codeFiltered'";
         $res = Database::query($sql);
         $course = Database::fetch_array($res);
-        $courseId = $course['id'];
+        $courseId = $course['id']; //int
 
         $count = 0;
         if (api_is_multiple_url_enabled()) {
@@ -2299,27 +2302,27 @@ class CourseManager
             }
 
             // Unsubscribe all users from the course
-            $sql = "DELETE FROM $table_course_user WHERE c_id='".$courseId."'";
+            $sql = "DELETE FROM $table_course_user WHERE c_id = $courseId";
             Database::query($sql);
             // Delete the course from the sessions tables
-            $sql = "DELETE FROM $table_session_course WHERE c_id='".$courseId."'";
+            $sql = "DELETE FROM $table_session_course WHERE c_id = $courseId";
             Database::query($sql);
-            $sql = "DELETE FROM $table_session_course_user WHERE c_id='".$courseId."'";
+            $sql = "DELETE FROM $table_session_course_user WHERE c_id = $courseId";
             Database::query($sql);
 
             // Delete from Course - URL
-            $sql = "DELETE FROM $table_course_rel_url WHERE c_id = '".$courseId."'";
+            $sql = "DELETE FROM $table_course_rel_url WHERE c_id = $courseId";
             Database::query($sql);
 
-            $sql = 'SELECT survey_id FROM '.$table_course_survey.' WHERE course_code="'.$codeFiltered.'"';
+            $sql = "SELECT survey_id FROM $table_course_survey WHERE course_code = '$codeFiltered'";
             $result_surveys = Database::query($sql);
             while ($surveys = Database::fetch_array($result_surveys)) {
-                $survey_id = $surveys[0];
-                $sql = 'DELETE FROM '.$table_course_survey_question.' WHERE survey_id="'.$survey_id.'"';
+                $survey_id = $surveys[0]; //int
+                $sql = "DELETE FROM $table_course_survey_question WHERE survey_id = $survey_id";
                 Database::query($sql);
-                $sql = 'DELETE FROM '.$table_course_survey_question_option.' WHERE survey_id="'.$survey_id.'"';
+                $sql = "DELETE FROM $table_course_survey_question_option WHERE survey_id = $survey_id";
                 Database::query($sql);
-                $sql = 'DELETE FROM '.$table_course_survey.' WHERE survey_id="'.$survey_id.'"';
+                $sql = "DELETE FROM $table_course_survey WHERE survey_id = $survey_id";
                 Database::query($sql);
             }
 
@@ -2368,7 +2371,7 @@ class CourseManager
             Database::query($sql);
 
             // Delete the course from the database
-            $sql = "DELETE FROM $table_course WHERE code = '".$codeFiltered."'";
+            $sql = "DELETE FROM $table_course WHERE code = '$codeFiltered'";
             Database::query($sql);
 
             // delete extra course fields
@@ -2456,18 +2459,21 @@ class CourseManager
 
         $course_title = Database::result(
             Database::query(
-                'SELECT title FROM '.$TABLECOURSE.' WHERE code="'.$course_code.'"'
+                "SELECT title FROM $TABLECOURSE WHERE code = '$course_code'"
             ),
             0,
             0
         );
+        if ($course_title === false) {
+            $course_title = '';
+        }
 
-        $sql = 'SELECT course.code as code, course.title as title, cu.sort as sort
-                FROM '.$TABLECOURSUSER.' as cu, '.$TABLECOURSE.' as course
-                WHERE   course.id = cu.c_id AND user_id = "'.$user_id.'" AND
-                        cu.relation_type<>'.COURSE_RELATION_TYPE_RRHH.' AND
+        $sql = "SELECT course.code as code, course.title as title, cu.sort as sort
+                FROM $TABLECOURSUSER as cu, $TABLECOURSE as course
+                WHERE   course.id = cu.c_id AND user_id = $user_id AND
+                        cu.relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
                         user_course_cat = 0
-                ORDER BY cu.sort';
+                ORDER BY cu.sort";
         $result = Database::query($sql);
 
         $course_title_precedent = '';
@@ -2484,21 +2490,21 @@ class CourseManager
                     $course_found = true;
                     $course_sort = $courses['sort'];
                     if ($counter == 0) {
-                        $sql = 'UPDATE '.$TABLECOURSUSER.'
+                        $sql = "UPDATE $TABLECOURSUSER
                                 SET sort = sort+1
                                 WHERE
-                                    user_id= "'.$user_id.'" AND
-                                    relation_type<>'.COURSE_RELATION_TYPE_RRHH.'
-                                    AND user_course_cat="0"
-                                    AND sort > "'.$course_sort.'"';
+                                    user_id= $user_id AND
+                                    relation_type <> ".COURSE_RELATION_TYPE_RRHH."
+                                    AND user_course_cat = 0
+                                    AND sort > $course_sort";
                         $course_sort++;
                     } else {
-                        $sql = 'UPDATE '.$TABLECOURSUSER.' SET sort = sort+1
+                        $sql = "UPDATE $TABLECOURSUSER SET sort = sort+1
                                 WHERE
-                                    user_id= "'.$user_id.'" AND
-                                    relation_type<>'.COURSE_RELATION_TYPE_RRHH.' AND
-                                    user_course_cat="0" AND
-                                    sort >= "'.$course_sort.'"';
+                                    user_id= $user_id AND
+                                    relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
+                                    user_course_cat = 0 AND
+                                    sort >= $course_sort";
                     }
                     Database::query($sql);
                     break;
@@ -2512,12 +2518,12 @@ class CourseManager
             if (!$course_found) {
                 $course_sort = Database::result(
                     Database::query(
-                        'SELECT min(sort) as min_sort FROM '.$TABLECOURSUSER.' WHERE user_id="'.$user_id.'" AND user_course_cat="0"'
+                        'SELECT min(sort) as min_sort FROM '.$TABLECOURSUSER.' WHERE user_id = "'.$user_id.'" AND user_course_cat="0"'
                     ),
                     0,
                     0
                 );
-                Database::query('UPDATE '.$TABLECOURSUSER.' SET sort = sort+1 WHERE user_id= "'.$user_id.'" AND user_course_cat="0"');
+                Database::query("UPDATE $TABLECOURSUSER SET sort = sort+1 WHERE user_id = $user_id AND user_course_cat = 0");
             }
         }
 
@@ -2527,14 +2533,15 @@ class CourseManager
     /**
      * check if course exists.
      *
-     * @param string $course_code
+     * @param string $courseCode
      *
      * @return int if exists, false else
      */
-    public static function course_exists($course_code)
+    public static function course_exists($courseCode)
     {
-        $sql = 'SELECT 1 FROM '.Database::get_main_table(TABLE_MAIN_COURSE).'
-                WHERE code="'.Database::escape_string($course_code).'"';
+        $courseCode = Database::escape_string($courseCode);
+        $sql = "SELECT 1 FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
+                WHERE code = '$courseCode'";
 
         return Database::num_rows(Database::query($sql));
     }
@@ -2562,15 +2569,15 @@ class CourseManager
 
         $name_course = $information['title'];
         $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." 
-                WHERE c_id ='".$courseId."'";
+                WHERE c_id = $courseId";
 
         // TODO: Ivan: This is a mistake, please, have a look at it. Intention here is diffcult to be guessed.
         //if ($send_to_tutor_also = true)
         // Proposed change:
         if ($send_to_tutor_also) {
-            $sql .= " AND is_tutor=1";
+            $sql .= " AND is_tutor = 1";
         } else {
-            $sql .= " AND status=1";
+            $sql .= " AND status = 1";
         }
 
         $result = Database::query($sql);
@@ -2657,7 +2664,7 @@ class CourseManager
                 WHERE
                     tcf.extra_field_type = $extraFieldType AND
                     tcf.variable = 'special_course' AND
-                    tcfv.value = 1  $where_access_url";
+                    tcfv.value = 1 $where_access_url";
 
         $result = Database::query($sql);
         $courseList = [];
@@ -2943,11 +2950,17 @@ class CourseManager
     public static function get_emails_of_tutors_to_course($courseId)
     {
         $list = [];
-        $res = Database::query("SELECT user_id FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
-                WHERE c_id ='".intval($courseId)."' AND status=1");
+        $courseId = intval($courseId);
+        $res = Database::query(
+            "SELECT user_id 
+            FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
+            WHERE c_id = $courseId AND status = 1"
+        );
         while ($list_users = Database::fetch_array($res)) {
-            $result = Database::query("SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
-                    WHERE user_id=".$list_users['user_id']);
+            $result = Database::query(
+                "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
+                WHERE user_id = ".$list_users['user_id']
+            );
             while ($row_user = Database::fetch_array($result)) {
                 $name_teacher = api_get_person_name($row_user['firstname'], $row_user['lastname']);
                 $list[] = [$row_user['email'] => $name_teacher];
@@ -2979,8 +2992,8 @@ class CourseManager
         $sql = "SELECT user_id
                 FROM $tbl_session_course_user
                 WHERE
-                    session_id = '$session_id' AND
-                    c_id = '$courseId' AND
+                    session_id = $session_id AND
+                    c_id = $courseId AND
                     status = 2
                 ";
         $rs = Database::query($sql);
@@ -3046,7 +3059,7 @@ class CourseManager
         $id = (int) $id;
         $table = Database::get_main_table(TABLE_MAIN_COURSE);
         $sql = "UPDATE $table SET $name = '".Database::escape_string($value)."'
-                WHERE id = '$id'";
+                WHERE id = $id";
 
         return Database::query($sql);
     }
@@ -3075,7 +3088,7 @@ class CourseManager
                 $i++;
             }
         }
-        $sql .= " WHERE id = '$id'";
+        $sql .= " WHERE id = $id";
 
         return Database::query($sql);
     }
@@ -3105,21 +3118,21 @@ class CourseManager
     }
 
     /**
-     * @param int $session_id
+     * @param int $sessionId
      *
      * @return mixed
      */
-    public static function get_session_category_id_by_session_id($session_id)
+    public static function get_session_category_id_by_session_id($sessionId)
     {
-        if (empty($session_id)) {
+        if (empty($sessionId)) {
             return [];
         }
-
+        $sessionId = intval($sessionId);
         $sql = 'SELECT sc.id session_category
                 FROM '.Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY).' sc
                 INNER JOIN '.Database::get_main_table(TABLE_MAIN_SESSION).' s
                 ON sc.id = s.session_category_id 
-                WHERE s.id="'.intval($session_id).'"';
+                WHERE s.id = '.$sessionId;
 
         return Database::result(
             Database::query($sql),
@@ -3243,20 +3256,20 @@ class CourseManager
                     ON (a.c_id = s.c_id)
                     WHERE
                         user_id = $hr_manager_id AND
-                        relation_type=".COURSE_RELATION_TYPE_RRHH." AND
-                        access_url_id = ".api_get_current_access_url_id()."";
+                        relation_type = ".COURSE_RELATION_TYPE_RRHH." AND
+                        access_url_id = ".api_get_current_access_url_id();
         } else {
             $sql = "SELECT c_id FROM $tbl_course_rel_user
-                    WHERE user_id = $hr_manager_id AND relation_type=".COURSE_RELATION_TYPE_RRHH." ";
+                    WHERE user_id = $hr_manager_id AND relation_type = ".COURSE_RELATION_TYPE_RRHH;
         }
         $result = Database::query($sql);
         if (Database::num_rows($result) > 0) {
             while ($row = Database::fetch_array($result)) {
                 $sql = "DELETE FROM $tbl_course_rel_user
                         WHERE
-                            c_id = '{$row['c_id']}' AND
+                            c_id = {$row['c_id']} AND
                             user_id = $hr_manager_id AND
-                            relation_type=".COURSE_RELATION_TYPE_RRHH." ";
+                            relation_type = ".COURSE_RELATION_TYPE_RRHH;
                 Database::query($sql);
             }
         }
@@ -3267,7 +3280,7 @@ class CourseManager
                 $courseInfo = api_get_course_info($course_code);
                 $courseId = $courseInfo['real_id'];
                 $sql = "INSERT IGNORE INTO $tbl_course_rel_user(c_id, user_id, status, relation_type)
-                        VALUES($courseId, $hr_manager_id, '".DRH."', '".COURSE_RELATION_TYPE_RRHH."')";
+                        VALUES($courseId, $hr_manager_id, ".DRH.", ".COURSE_RELATION_TYPE_RRHH.")";
                 $result = Database::query($sql);
                 if (Database::affected_rows($result)) {
                     $affected_rows++;
@@ -3354,7 +3367,7 @@ class CourseManager
         $whereConditions = '';
         switch ($status) {
             case COURSEMANAGER:
-                $whereConditions .= " AND cru.user_id = '$user_id'";
+                $whereConditions .= " AND cru.user_id = $user_id";
                 if (!$showAllAssignedCourses) {
                     $whereConditions .= " AND cru.status = ".COURSEMANAGER;
                 } else {
@@ -3363,7 +3376,7 @@ class CourseManager
                 break;
             case DRH:
                 $whereConditions .= " AND
-                    cru.user_id = '$user_id' AND
+                    cru.user_id = $user_id AND
                     cru.status = ".DRH." AND
                     relation_type = '".COURSE_RELATION_TYPE_RRHH."'
                 ";
@@ -4062,14 +4075,14 @@ 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_course_cat = '".$user_category_id."'
+                    course_rel_user.user_id = $user_id AND
+                    course_rel_user.user_course_cat = $user_category_id
                     $without_special_courses ";
 
         // 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 url.c_id = course.id AND access_url_id='".$current_url_id."'";
+            $sql .= " AND url.c_id = course.id 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";
@@ -4206,7 +4219,7 @@ class CourseManager
                 LEFT JOIN $tblUserCategory ucc
                 ON cru.user_course_cat = ucc.id
                 WHERE
-                    cru.user_id = $userId AND c_id= $courseId ";
+                    cru.user_id = $userId AND c_id = $courseId ";
 
         $res = Database::query($sql);
 
@@ -5017,7 +5030,7 @@ class CourseManager
         $table_course_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
         $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
         $table_course_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
-
+        $urlId = api_get_current_access_url_id();
         //$table_course_access table uses the now() and interval ...
         $now = api_get_utc_datetime();
         $sql = "SELECT COUNT(course_access_id) course_count, a.c_id, visibility
@@ -5027,10 +5040,11 @@ class CourseManager
                 INNER JOIN $table_course_url u
                 ON u.c_id = c.id
                 WHERE
-                    u.access_url_id = ".api_get_current_access_url_id()." AND
+                    u.access_url_id = $urlId AND
                     login_course_date <= '$now' AND
                     login_course_date > DATE_SUB('$now', INTERVAL $days DAY) AND
-                    visibility <> '".COURSE_VISIBILITY_CLOSED."' AND visibility <> '".COURSE_VISIBILITY_HIDDEN."'
+                    visibility <> ".COURSE_VISIBILITY_CLOSED." AND
+                    visibility <> ".COURSE_VISIBILITY_HIDDEN." 
                 GROUP BY a.c_id
                 ORDER BY course_count DESC
                 LIMIT $limit
@@ -5285,49 +5299,6 @@ class CourseManager
         return $visibilityCondition;
     }
 
-    /**
-     * Get available le courses count.
-     *
-     * @param int $accessUrlId (optional)
-     *
-     * @return int Number of courses
-     */
-    public static function countAvailableCourses($accessUrlId = 1)
-    {
-        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
-        $tableCourseRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
-        $specialCourseList = self::get_special_course_list();
-
-        $withoutSpecialCourses = '';
-        if (!empty($specialCourseList)) {
-            $withoutSpecialCourses = ' AND c.id NOT IN ("'.implode('","', $specialCourseList).'")';
-        }
-
-        $visibilityCondition = self::getCourseVisibilitySQLCondition('c', true);
-
-        $accessUrlId = (int) $accessUrlId;
-        if (empty($accessUrlId)) {
-            $accessUrlId = 1;
-        }
-
-        $sql = "SELECT count(c.id) 
-                FROM $tableCourse c 
-                INNER JOIN $tableCourseRelAccessUrl u
-                ON (c.id = u.c_id)
-                WHERE
-                    u.access_url_id = $accessUrlId AND
-                    c.visibility != 0 AND
-                    c.visibility != 4
-                    $withoutSpecialCourses
-                    $visibilityCondition
-                ";
-
-        $res = Database::query($sql);
-        $row = Database::fetch_row($res);
-
-        return $row[0];
-    }
-
     /**
      * Return a link to go to the course, validating the visibility of the
      * course and the user status.
@@ -5449,8 +5420,8 @@ class CourseManager
             }
 
             // Recover user categories
-            $sql = 'SELECT * FROM '.$course_user_table.'
-                    WHERE c_id ="'.$courseId.'" AND status="1" AND relation_type = 0 '.$cond;
+            $sql = "SELECT * FROM $course_user_table
+                    WHERE c_id = $courseId AND status = 1 AND relation_type = 0 ".$cond;
             $result = Database::query($sql);
             if (Database::num_rows($result)) {
                 $teachersToDelete = Database::store_result($result, 'ASSOC');
@@ -5460,8 +5431,8 @@ class CourseManager
                 }
             }
 
-            $sql = 'DELETE FROM '.$course_user_table.'
-                    WHERE c_id ="'.$courseId.'" AND status="1" AND relation_type = 0 '.$cond;
+            $sql = "DELETE FROM $course_user_table
+                    WHERE c_id = $courseId AND status = 1 AND relation_type = 0 ".$cond;
 
             Database::query($sql);
         }
@@ -5470,13 +5441,13 @@ class CourseManager
             foreach ($teachers as $userId) {
                 $userId = intval($userId);
                 // We check if the teacher is already subscribed in this course
-                $sql = 'SELECT 1 FROM '.$course_user_table.'
-                        WHERE user_id = "'.$userId.'" AND c_id = "'.$courseId.'" ';
+                $sql = "SELECT 1 FROM $course_user_table
+                        WHERE user_id = $userId AND c_id = $courseId";
                 $result = Database::query($sql);
                 if (Database::num_rows($result)) {
-                    $sql = 'UPDATE '.$course_user_table.' 
-                            SET status = "1"
-                            WHERE c_id = "'.$courseId.'" AND user_id = "'.$userId.'"  ';
+                    $sql = "UPDATE $course_user_table 
+                            SET status = 1
+                            WHERE c_id = $courseId AND user_id = $userId ";
                 } else {
                     $userCourseCategory = '0';
                     if (isset($teacherBackup[$userId]) &&
@@ -5492,11 +5463,11 @@ class CourseManager
                     $sql = "INSERT INTO $course_user_table SET
                             c_id = $courseId,
                             user_id = $userId,
-                            status = '1',
-                            is_tutor = '0',
-                            sort = '0',
-                            relation_type = '0',
-                            user_course_cat = '$userCourseCategory'
+                            status = 1,
+                            is_tutor = 0,
+                            sort = 0,
+                            relation_type = 0,
+                            user_course_cat = $userCourseCategory
                     ";
                 }
                 Database::query($sql);
@@ -5599,6 +5570,8 @@ class CourseManager
             // Get send_mail_setting (auth)from table
             'email_alert_to_teacher_on_new_user_in_course',
             'enable_lp_auto_launch',
+            'enable_exercise_auto_launch',
+            'enable_document_auto_launch',
             'pdf_export_watermark_text',
             'show_system_folders',
             'exercise_invisible_in_session',
@@ -5607,7 +5580,8 @@ class CourseManager
             'email_to_teachers_on_new_work_feedback',
         ];
 
-        if (!empty(ExerciseLib::getScoreModels())) {
+        $courseModels = ExerciseLib::getScoreModels();
+        if (!empty($courseModels)) {
             $courseSettings[] = 'score_model_id';
         }
 
@@ -5890,9 +5864,10 @@ class CourseManager
     public static function getCourseNameFromCode($code)
     {
         $tbl_main_categories = Database::get_main_table(TABLE_MAIN_COURSE);
-        $sql = 'SELECT title
-                FROM '.$tbl_main_categories.'
-                WHERE code = "'.Database::escape_string($code).'"';
+        $code = Database::escape_string($code);
+        $sql = "SELECT title
+                FROM $tbl_main_categories
+                WHERE code = '$code'";
         $result = Database::query($sql);
         if ($col = Database::fetch_array($result)) {
             return $col['title'];
@@ -6165,7 +6140,7 @@ class CourseManager
      *
      * @param int  Category ID
      *
-     * @return string int
+     * @return string Course code
      */
     public static function get_course_by_category($category_id)
     {
@@ -6272,12 +6247,16 @@ class CourseManager
      * http://chamilodev.beeznest.com/main/auth/inscription.php?c=ABC&e=3
      * Where "c" is the course code and "e" is the exercise Id, after a successful
      * registration the user will be sent to the course or exercise
+     *
+     * @param array $form_data
+     *
+     * @return array
      */
     public static function redirectToCourse($form_data)
     {
         $course_code_redirect = Session::read('course_redirect');
         $_user = api_get_user_info();
-        $user_id = api_get_user_id();
+        $userId = api_get_user_id();
 
         if (!empty($course_code_redirect)) {
             $course_info = api_get_course_info($course_code_redirect);
@@ -6287,7 +6266,7 @@ class CourseManager
                     [COURSE_VISIBILITY_OPEN_PLATFORM, COURSE_VISIBILITY_OPEN_WORLD]
                 )
                 ) {
-                    if (self::is_user_subscribed_in_course($user_id, $course_info['code'])) {
+                    if (self::is_user_subscribed_in_course($userId, $course_info['code'])) {
                         $form_data['action'] = $course_info['course_public_url'];
                         $form_data['message'] = sprintf(get_lang('YouHaveBeenRegisteredToCourseX'), $course_info['title']);
                         $form_data['button'] = Display::button(
@@ -6303,7 +6282,8 @@ class CourseManager
                         $result = $objExercise->read($exercise_redirect);
 
                         if (!empty($exercise_redirect) && !empty($result)) {
-                            $form_data['action'] = api_get_path(WEB_CODE_PATH).'exercise/overview.php?exerciseId='.$exercise_redirect.'&cidReq='.$course_info['code'];
+                            $form_data['action'] = api_get_path(WEB_CODE_PATH).
+                                'exercise/overview.php?exerciseId='.$exercise_redirect.'&cidReq='.$course_info['code'];
                             $form_data['message'] .= '<br />'.get_lang('YouCanAccessTheExercise');
                             $form_data['button'] = Display::button(
                                 'next',
@@ -6327,8 +6307,7 @@ class CourseManager
     /**
      * Return tab of params to display a course title in the My Courses tab
      * Check visibility, right, and notification icons, and load_dirs option
-     *  // get html course params
-     *   //.
+     * get html course params.
      *
      * @param $courseId
      * @param bool $loadDirs
@@ -6337,7 +6316,8 @@ class CourseManager
      */
     public static function getCourseParamsForDisplay($courseId, $loadDirs = false)
     {
-        $user_id = api_get_user_id();
+        $userId = api_get_user_id();
+        $courseId = intval($courseId);
         // Table definitions
         $TABLECOURS = Database::get_main_table(TABLE_MAIN_COURSE);
         $TABLECOURSUSER = Database::get_main_table(TABLE_MAIN_COURSE_USER);
@@ -6369,15 +6349,15 @@ class CourseManager
                 INNER JOIN $TABLE_ACCESS_URL_REL_COURSE url
                 ON (url.c_id = course.id)
                 WHERE
-                    course.id=".intval($courseId)." AND
-                    course_rel_user.user_id = ".intval($user_id)."
+                    course.id = $courseId AND
+                    course_rel_user.user_id = $userId
                     $without_special_courses
                 ";
 
         // 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 url.c_id = course.id AND access_url_id=".intval($current_url_id);
+            $sql .= " AND url.c_id = course.id 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";

+ 16 - 249
main/inc/lib/course_category.lib.php

@@ -186,9 +186,17 @@ class CourseCategory
 
     /**
      * @param string $node
+     *
+     * @return bool
      */
     public static function deleteNode($node)
     {
+        $category = self::getCategory($node);
+
+        if (empty($category)) {
+            return false;
+        }
+
         $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
         $tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
         $node = Database::escape_string($node);
@@ -205,12 +213,18 @@ class CourseCategory
                 Database::query("UPDATE $tbl_category SET parent_id=NULL WHERE parent_id='$node'");
             }
 
+            $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
+            $sql = "DELETE FROM $table WHERE course_category_id = ".$category['id'];
+
+            Database::query($sql);
             Database::query("UPDATE $tbl_category SET tree_pos=tree_pos-1 WHERE tree_pos > '".$row['tree_pos']."'");
             Database::query("DELETE FROM $tbl_category WHERE code='$node'");
 
             if (!empty($row['parent_id'])) {
                 self::updateParentCategoryChildrenCount($row['parent_id'], -1);
             }
+
+            return true;
         }
     }
 
@@ -559,54 +573,6 @@ class CourseCategory
         return $categories;
     }
 
-    /**
-     * @return array
-     */
-    public static function browseCourseCategories()
-    {
-        $url_access_id = 1;
-        if (api_is_multiple_url_enabled()) {
-            $url_access_id = api_get_current_access_url_id();
-        }
-
-        $countCourses = CourseManager::countAvailableCourses($url_access_id);
-
-        $categories = [];
-        $categories[0][0] = [
-            'id' => 0,
-            'name' => get_lang('DisplayAll'),
-            'code' => 'ALL',
-            'parent_id' => null,
-            'tree_pos' => 0,
-            'count_courses' => $countCourses,
-        ];
-
-        $categoriesFromDatabase = self::getCategories();
-        foreach ($categoriesFromDatabase as $row) {
-            $count_courses = self::countCoursesInCategory($row['code']);
-            $row['count_courses'] = $count_courses;
-            if (empty($row['parent_id'])) {
-                $categories[0][$row['tree_pos']] = $row;
-            } else {
-                $categories[$row['parent_id']][$row['tree_pos']] = $row;
-            }
-        }
-        $count_courses = self::countCoursesInCategory();
-        $categories[0][count($categories[0]) + 1] = [
-            'id' => 0,
-            'name' => get_lang('None'),
-            'code' => 'NONE',
-            'parent_id' => null,
-            'tree_pos' => $row['tree_pos'] + 1,
-            'children_count' => 0,
-            'auth_course_child' => true,
-            'auth_cat_child' => true,
-            'count_courses' => $count_courses,
-        ];
-
-        return $categories;
-    }
-
     /**
      * @param string $category_code
      * @param string $searchTerm
@@ -618,13 +584,7 @@ class CourseCategory
         $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
         $categoryCode = Database::escape_string($category_code);
         $searchTerm = Database::escape_string($searchTerm);
-
-        $specialCourseList = CourseManager::get_special_course_list();
-        $without_special_courses = '';
-        if (!empty($specialCourseList)) {
-            $without_special_courses = ' AND course.id NOT IN ("'.implode('","', $specialCourseList).'")';
-        }
-
+        $avoidCoursesCondition = CoursesAndSessionsCatalog::getAvoidCourseCondition();
         $visibilityCondition = CourseManager::getCourseVisibilitySQLCondition(
             'course',
             true
@@ -660,172 +620,13 @@ class CourseCategory
                     course.visibility != '4'
                     $categoryFilter
                     $searchFilter
-                    $without_special_courses
+                    $avoidCoursesCondition
                     $visibilityCondition
             ";
 
         return Database::num_rows(Database::query($sql));
     }
 
-    /**
-     * @param string $category_code
-     * @param int    $random_value
-     * @param array  $limit         will be used if $random_value is not set.
-     *                              This array should contains 'start' and 'length' keys
-     *
-     * @return array
-     */
-    public static function browseCoursesInCategory($category_code, $random_value = null, $limit = [])
-    {
-        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
-        $specialCourseList = CourseManager::get_special_course_list();
-        $without_special_courses = '';
-        if (!empty($specialCourseList)) {
-            $without_special_courses = ' AND course.id NOT IN ("'.implode('","', $specialCourseList).'")';
-        }
-        $visibilityCondition = CourseManager::getCourseVisibilitySQLCondition(
-            'course',
-            true
-        );
-
-        if (!empty($random_value)) {
-            $random_value = intval($random_value);
-
-            $sql = "SELECT COUNT(*) FROM $tbl_course";
-            $result = Database::query($sql);
-            list($num_records) = Database::fetch_row($result);
-
-            if (api_is_multiple_url_enabled()) {
-                $url_access_id = api_get_current_access_url_id();
-                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
-
-                $sql = "SELECT COUNT(*) FROM $tbl_course course
-                        INNER JOIN $tbl_url_rel_course as url_rel_course
-                        ON (url_rel_course.c_id = course.id)
-                        WHERE access_url_id = $url_access_id ";
-                $result = Database::query($sql);
-                list($num_records) = Database::fetch_row($result);
-
-                $sql = "SELECT course.id, course.id as real_id 
-                        FROM $tbl_course course
-                        INNER JOIN $tbl_url_rel_course as url_rel_course
-                        ON (url_rel_course.c_id = course.id)
-                        WHERE
-                            access_url_id = $url_access_id AND
-                            RAND()*$num_records< $random_value
-                            $without_special_courses 
-                            $visibilityCondition
-                        ORDER BY RAND()
-                        LIMIT 0, $random_value";
-            } else {
-                $sql = "SELECT id, id as real_id FROM $tbl_course course
-                        WHERE 
-                            RAND()*$num_records< $random_value 
-                            $without_special_courses 
-                            $visibilityCondition
-                        ORDER BY RAND()
-                        LIMIT 0, $random_value";
-            }
-
-            $result = Database::query($sql);
-            $id_in = null;
-            while (list($id) = Database::fetch_row($result)) {
-                if ($id_in) {
-                    $id_in .= ",$id";
-                } else {
-                    $id_in = "$id";
-                }
-            }
-            if ($id_in === null) {
-                return [];
-            }
-            $sql = "SELECT *, id as real_id FROM $tbl_course WHERE id IN($id_in)";
-        } else {
-            $limitFilter = self::getLimitFilterFromArray($limit);
-            $category_code = Database::escape_string($category_code);
-            if (empty($category_code) || $category_code == "ALL") {
-                $sql = "SELECT *, id as real_id FROM $tbl_course
-                    WHERE
-                        1=1
-                        $without_special_courses
-                        $visibilityCondition
-                    ORDER BY title $limitFilter ";
-            } else {
-                if ($category_code == 'NONE') {
-                    $category_code = '';
-                }
-                $sql = "SELECT *, id as real_id FROM $tbl_course
-                        WHERE
-                            category_code='$category_code'
-                            $without_special_courses
-                            $visibilityCondition
-                        ORDER BY title $limitFilter ";
-            }
-
-            // Showing only the courses of the current Chamilo access_url_id
-            if (api_is_multiple_url_enabled()) {
-                $url_access_id = api_get_current_access_url_id();
-                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
-                if ($category_code != "ALL") {
-                    $sql = "SELECT *, course.id real_id FROM $tbl_course as course
-                            INNER JOIN $tbl_url_rel_course as url_rel_course
-                            ON (url_rel_course.c_id = course.id)
-                            WHERE
-                                access_url_id = $url_access_id AND
-                                category_code='$category_code'
-                                $without_special_courses
-                                $visibilityCondition
-                            ORDER BY title $limitFilter";
-                } else {
-                    $sql = "SELECT *, course.id real_id FROM $tbl_course as course
-                            INNER JOIN $tbl_url_rel_course as url_rel_course
-                            ON (url_rel_course.c_id = course.id)
-                            WHERE
-                                access_url_id = $url_access_id
-                                $without_special_courses
-                                $visibilityCondition
-                            ORDER BY title $limitFilter";
-                }
-            }
-        }
-
-        $result = Database::query($sql);
-        $courses = [];
-        while ($row = Database::fetch_array($result)) {
-            $row['registration_code'] = !empty($row['registration_code']);
-            $count_users = CourseManager::get_users_count_in_course($row['code']);
-            $count_connections_last_month = Tracking::get_course_connections_count(
-                $row['id'],
-                0,
-                api_get_utc_datetime(time() - (30 * 86400))
-            );
-
-            if ($row['tutor_name'] == '0') {
-                $row['tutor_name'] = get_lang('NoManager');
-            }
-            $point_info = CourseManager::get_course_ranking($row['id'], 0);
-            $courses[] = [
-                'real_id' => $row['real_id'],
-                'point_info' => $point_info,
-                'code' => $row['code'],
-                'directory' => $row['directory'],
-                'visual_code' => $row['visual_code'],
-                'title' => $row['title'],
-                'tutor' => $row['tutor_name'],
-                'subscribe' => $row['subscribe'],
-                'unsubscribe' => $row['unsubscribe'],
-                'registration_code' => $row['registration_code'],
-                'creation_date' => $row['creation_date'],
-                'visibility' => $row['visibility'],
-                'category' => $row['category_code'],
-                'count_users' => $count_users,
-                'count_connections' => $count_connections_last_month,
-            ];
-        }
-
-        return $courses;
-    }
-
     /**
      * create recursively all categories as option of the select passed in parameter.
      *
@@ -955,40 +756,6 @@ class CourseCategory
         return Database::store_result($result, 'ASSOC');
     }
 
-    /**
-     * @return array
-     */
-    public static function getLimitArray()
-    {
-        $pageCurrent = isset($_REQUEST['pageCurrent']) ? intval($_GET['pageCurrent']) : 1;
-        $pageLength = isset($_REQUEST['pageLength']) ? intval($_GET['pageLength']) : CoursesAndSessionsCatalog::PAGE_LENGTH;
-
-        return [
-            'start' => ($pageCurrent - 1) * $pageLength,
-            'current' => $pageCurrent,
-            'length' => $pageLength,
-        ];
-    }
-
-    /**
-     * Return LIMIT to filter SQL query.
-     *
-     * @param array $limit
-     *
-     * @return string
-     */
-    public static function getLimitFilterFromArray($limit)
-    {
-        $limitFilter = '';
-        if (!empty($limit) && is_array($limit)) {
-            $limitStart = isset($limit['start']) ? $limit['start'] : 0;
-            $limitLength = isset($limit['length']) ? $limit['length'] : 12;
-            $limitFilter = 'LIMIT '.$limitStart.', '.$limitLength;
-        }
-
-        return $limitFilter;
-    }
-
     /**
      * Get Pagination HTML div.
      *

+ 1 - 2
main/inc/lib/display.lib.php

@@ -1682,7 +1682,7 @@ class Display
      * @return array Empty array or session array
      *               ['title'=>'...','category'=>'','dates'=>'...','coach'=>'...','active'=>true/false,'session_category_id'=>int]
      */
-    public static function get_session_title_box($session_id)
+    public static function getSessionTitleBox($session_id)
     {
         global $nosession;
 
@@ -1690,7 +1690,6 @@ class Display
             global $now, $date_start, $date_end;
         }
         $output = [];
-        $active = false;
         if (!$nosession) {
             $main_user_table = Database::get_main_table(TABLE_MAIN_USER);
             $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);

+ 56 - 126
main/inc/lib/document.lib.php

@@ -104,6 +104,7 @@ class DocumentManager
             'etx' => 'text/x-setext',
             'exe' => 'application/octet-stream',
             'ez' => 'application/andrew-inset',
+            'flv' => 'video/flv',
             'gif' => 'image/gif',
             'gtar' => 'application/x-gtar',
             'gz' => 'application/x-gzip',
@@ -375,6 +376,7 @@ class DocumentManager
                     header('Content-type: application/octet-stream');
                     break;
             }
+
             header('Content-type: '.$contentType);
             header('Content-Length: '.$len);
             $user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
@@ -3076,29 +3078,16 @@ class DocumentManager
      *
      * @return string
      */
-    public static function generate_jplayer_jquery($params = [])
+    public static function generateAudioJavascript($params = [])
     {
-        $js_path = api_get_path(WEB_LIBRARY_PATH).'javascript/';
-
         $js = '
-            $("#jquery_jplayer_'.$params['count'].'").jPlayer({
-                ready: function() {
-                    $(this).jPlayer("setMedia", {
-                        '.$params['extension'].' : "'.$params['url'].'"
-                    });
-                },
-                play: function() { // To avoid both jPlayers playing together.
-                    $(this).jPlayer("pauseOthers");
-                },
-                //errorAlerts: true,
-                //warningAlerts: true,
-                swfPath: "'.$js_path.'jquery-jplayer/jplayer/",
-                //supplied: "m4a, oga, mp3, ogg, wav",
-                supplied: "'.$params['extension'].'",
-                wmode: "window",
-                solution: "flash, html",  // Do not change this setting
-                cssSelectorAncestor: "#jp_container_'.$params['count'].'",
-            });  	 '."\n\n";
+            $(\'audio.audio_preview\').mediaelementplayer({
+                features: [\'playpause\'],
+                audioWidth: 30,
+                audioHeight: 30,
+                success: function(mediaElement, originalNode, instance) {                
+                }
+            });';
 
         return $js;
     }
@@ -3106,94 +3095,34 @@ class DocumentManager
     /**
      * Shows a play icon next to the document title in the document list.
      *
-     * @param int
-     * @param string
+     * @param string $documentWebPath
+     * @param array  $documentInfo
      *
-     * @return string html content
+     * @return string
      */
-    public static function generate_media_preview($i, $type = 'simple')
+    public static function generateAudioPreview($documentWebPath, $documentInfo)
     {
-        $i = intval($i);
-        $extra_controls = $progress = '';
-        if ($type == 'advanced') {
-            $extra_controls = ' <li><a href="javascript:;" class="jp-stop" tabindex="1">stop</a></li>
-                                <li><a href="#" class="jp-mute" tabindex="1">mute</a></li>
-                                <li><a href="#" class="jp-unmute" tabindex="1">unmute</a></li>';
-            $progress = '<div class="jp-progress">
-                                <div class="jp-seek-bar">
-                                    <div class="jp-play-bar"></div>
-                                </div>
-                            </div>';
-        }
-
-        //Shows only the play button
-        $html = '<div id="jquery_jplayer_'.$i.'" class="jp-jplayer"></div>
-                <div id="jp_container_'.$i.'" class="jp-audio">
-                    <div class="jp-type-single">
-                        <div class="jp-gui jp-interface">
-                            <ul class="jp-controls">
-                                <li><a href="javascript:;" class="jp-play" tabindex="1">play</a></li>
-                                <li><a href="javascript:;" class="jp-pause" tabindex="1">pause</a></li>
-                                '.$extra_controls.'
-                            </ul>
-                            '.$progress.'
-                        </div>
-                    </div>
-                </div>';
+        $filePath = $documentWebPath.$documentInfo['path'];
+        $extension = $documentInfo['file_extension'];
+        $html = '<span class="preview"> <audio class="audio_preview skip" src="'.$filePath.'" type="audio/'.$extension.'" > </audio></span>';
 
         return $html;
     }
 
     /**
-     * @param array $document_data
+     * @param string $file
+     * @param string $extension
      *
      * @return string
      */
-    public static function generate_video_preview($document_data = [])
+    public static function generateVideoPreview($file, $extension)
     {
-        $html = '
-        <div id="jp_container_1" class="jp-video center-block" role="application" aria-label="media player">
-            <div class="jp-type-single">
-                <div id="jquery_jplayer_1" class="jp-jplayer"></div>
-                <div class="jp-gui">
-                    <div class="jp-video-play">
-                    </div>
-                    <div class="jp-interface">
-                        <div class="jp-progress">
-                            <div class="jp-seek-bar">
-                                <div class="jp-play-bar"></div>
-                            </div>
-                        </div>
-                        <div class="jp-current-time" role="timer" aria-label="time">&nbsp;</div>
-                        <div class="jp-duration" role="timer" aria-label="duration">&nbsp;</div>
-                        <div class="jp-controls-holder">
-                          <div class="jp-controls">
-                            <button class="jp-play" role="button" tabindex="0">play</button>
-                            <button class="jp-stop" role="button" tabindex="0">stop</button>
-                          </div>
-                          <div class="jp-volume-controls">
-                            <button class="jp-mute" role="button" tabindex="0">mute</button>
-                            <button class="jp-volume-max" role="button" tabindex="0">max volume</button>
-                            <div class="jp-volume-bar">
-                                <div class="jp-volume-bar-value"></div>
-                            </div>
-                          </div>
-                          <div class="jp-toggles">
-                            <button class="jp-repeat" role="button" tabindex="0">repeat</button>
-                            <button class="jp-full-screen" role="button" tabindex="0">full screen</button>
-                          </div>
-                        </div>
-                        <div class="jp-details">
-                          <div class="jp-title" aria-label="title">&nbsp;</div>
-                        </div>
-                    </div>
-                </div>
-                <div class="jp-no-solution">
-                    <span>'.get_lang('UpdateRequire').'</span>
-                    '.get_lang("ToPlayTheMediaYouWillNeedToUpdateYourBrowserToARecentVersionYouCanAlsoDownloadTheFile").'
-                </div>
-            </div>
-        </div>';
+        $type = '';
+        /*if ($extension != 'flv') {
+
+        }*/
+        //$type = "video/$extension";
+        $html = '<video id="myvideo"  src="'.$file.'" controls '.$type.'">';
 
         return $html;
     }
@@ -4971,20 +4900,20 @@ class DocumentManager
     /**
      * Create a html hyperlink depending on if it's a folder or a file.
      *
-     * @param array $document_data
-     * @param array $course_info
-     * @param bool  $show_as_icon      - if it is true, only a clickable icon will be shown
-     * @param int   $visibility        (1/0)
-     * @param int   $counter
-     * @param int   $size
-     * @param bool  $isAllowedToEdit
-     * @param bool  $isCertificateMode
+     * @param string $documentWebPath
+     * @param array  $document_data
+     * @param bool   $show_as_icon      - if it is true, only a clickable icon will be shown
+     * @param int    $visibility        (1/0)
+     * @param int    $counter
+     * @param int    $size
+     * @param bool   $isAllowedToEdit
+     * @param bool   $isCertificateMode
      *
      * @return string url
      */
     public static function create_document_link(
+        $documentWebPath,
         $document_data,
-        $course_info,
         $show_as_icon = false,
         $counter = null,
         $visibility,
@@ -4993,10 +4922,10 @@ class DocumentManager
         $isCertificateMode = false
     ) {
         global $dbl_click_id;
+        $www = $documentWebPath;
 
-        $current_session_id = api_get_session_id();
+        $sessionId = api_get_session_id();
         $courseParams = api_get_cidreq();
-        $www = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/document';
         $webODFList = self::get_web_odf_extension_list();
 
         // Get the title or the basename depending on what we're using
@@ -5019,11 +4948,11 @@ class DocumentManager
 
         if (!$show_as_icon) {
             // Build download link (icon)
-            $forcedownload_link = ($filetype == 'folder') ? api_get_self().'?'.$courseParams.'&action=downloadfolder&id='.$document_data['id'] : api_get_self().'?'.$courseParams.'&amp;action=download&amp;id='.$document_data['id'];
+            $forcedownload_link = $filetype == 'folder' ? api_get_self().'?'.$courseParams.'&action=downloadfolder&id='.$document_data['id'] : api_get_self().'?'.$courseParams.'&amp;action=download&amp;id='.$document_data['id'];
             // Folder download or file download?
-            $forcedownload_icon = ($filetype == 'folder') ? 'save_pack.png' : 'save.png';
+            $forcedownload_icon = $filetype == 'folder' ? 'save_pack.png' : 'save.png';
             // Prevent multiple clicks on zipped folder download
-            $prevent_multiple_click = ($filetype == 'folder') ? " onclick=\"javascript: if(typeof clic_$dbl_click_id == 'undefined' || !clic_$dbl_click_id) { clic_$dbl_click_id=true; window.setTimeout('clic_".($dbl_click_id++)."=false;',10000); } else { return false; }\"" : '';
+            $prevent_multiple_click = $filetype == 'folder' ? " onclick=\"javascript: if(typeof clic_$dbl_click_id == 'undefined' || !clic_$dbl_click_id) { clic_$dbl_click_id=true; window.setTimeout('clic_".($dbl_click_id++)."=false;',10000); } else { return false; }\"" : '';
         }
 
         $target = '_self';
@@ -5091,6 +5020,8 @@ class DocumentManager
         $curdirpath = isset($_GET['curdirpath']) ? Security::remove_XSS($_GET['curdirpath']) : null;
         $send_to = null;
         $checkExtension = $path;
+        $extension = pathinfo($path, PATHINFO_EXTENSION);
+        $document_data['file_extension'] = $extension;
 
         if (!$show_as_icon) {
             if ($filetype == 'folder') {
@@ -5099,9 +5030,9 @@ class DocumentManager
                     api_get_setting('students_download_folders') == 'true'
                 ) {
                     // filter: when I am into a shared folder, I can only show "my shared folder" for donwload
-                    if (self::is_shared_folder($curdirpath, $current_session_id)) {
+                    if (self::is_shared_folder($curdirpath, $sessionId)) {
                         if (preg_match('/shared_folder\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
-                            preg_match('/shared_folder_session_'.$current_session_id.'\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
+                            preg_match('/shared_folder_session_'.$sessionId.'\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
                             $isAllowedToEdit || api_is_platform_admin()
                         ) {
                             $force_download_html = ($size == 0) ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.'>'.
@@ -5136,7 +5067,6 @@ class DocumentManager
             }
 
             $pdf_icon = '';
-            $extension = pathinfo($path, PATHINFO_EXTENSION);
             if (!$isAllowedToEdit &&
                 api_get_setting('students_export2pdf') == 'true' &&
                 $filetype == 'file' &&
@@ -5152,7 +5082,7 @@ class DocumentManager
             }
 
             if ($filetype == 'file') {
-                // Sound preview with jplayer
+                // Sound preview
                 if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
                     (preg_match('/wav$/i', urldecode($checkExtension))) ||
                     preg_match('/ogg$/i', urldecode($checkExtension))
@@ -5214,16 +5144,16 @@ class DocumentManager
             // Icon column
             if (preg_match('/shared_folder/', urldecode($checkExtension)) &&
                 preg_match('/shared_folder$/', urldecode($checkExtension)) == false &&
-                preg_match('/shared_folder_session_'.$current_session_id.'$/', urldecode($url)) == false
+                preg_match('/shared_folder_session_'.$sessionId.'$/', urldecode($url)) == false
             ) {
                 if ($filetype == 'file') {
-                    //Sound preview with jplayer
+                    //Sound preview
                     if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
                         (preg_match('/wav$/i', urldecode($checkExtension))) ||
                         preg_match('/ogg$/i', urldecode($checkExtension))) {
-                        $sound_preview = self::generate_media_preview($counter);
+                        $soundPreview = self::generateAudioPreview($documentWebPath, $document_data);
 
-                        return $sound_preview;
+                        return $soundPreview;
                     } elseif (
                         // Show preview
                         preg_match('/swf$/i', urldecode($checkExtension)) ||
@@ -5258,9 +5188,9 @@ class DocumentManager
                     if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
                         (preg_match('/wav$/i', urldecode($checkExtension))) ||
                         preg_match('/ogg$/i', urldecode($checkExtension))) {
-                        $sound_preview = self::generate_media_preview($counter);
+                        $soundPreview = self::generateAudioPreview($documentWebPath, $document_data);
 
-                        return $sound_preview;
+                        return $soundPreview;
                     } elseif (
                         //Show preview
                         preg_match('/html$/i', urldecode($checkExtension)) ||
@@ -5303,7 +5233,7 @@ class DocumentManager
     public static function build_document_icon_tag($type, $path, $isAllowedToEdit = null)
     {
         $basename = basename($path);
-        $current_session_id = api_get_session_id();
+        $sessionId = api_get_session_id();
         if (is_null($isAllowedToEdit)) {
             $isAllowedToEdit = api_is_allowed_to_edit(null, true);
         }
@@ -5328,7 +5258,7 @@ class DocumentManager
                 $basename = get_lang('UserFolder').' '.$userInfo['complete_name'];
                 $user_image = true;
             } elseif (strstr($path, 'shared_folder_session_')) {
-                $sessionName = api_get_session_name($current_session_id);
+                $sessionName = api_get_session_name($sessionId);
                 if ($isAllowedToEdit) {
                     $basename = '***('.$sessionName.')*** '.get_lang('HelpUsersFolder');
                 } else {
@@ -5693,16 +5623,16 @@ class DocumentManager
      * Checks whether the user is in shared folder.
      *
      * @param string $curdirpath
-     * @param int    $current_session_id
+     * @param int    $sessionId
      *
      * @return bool Return true when user is into shared folder
      */
-    public static function is_shared_folder($curdirpath, $current_session_id)
+    public static function is_shared_folder($curdirpath, $sessionId)
     {
         $clean_curdirpath = Security::remove_XSS($curdirpath);
         if ($clean_curdirpath == '/shared_folder') {
             return true;
-        } elseif ($clean_curdirpath == '/shared_folder_session_'.$current_session_id) {
+        } elseif ($clean_curdirpath == '/shared_folder_session_'.$sessionId) {
             return true;
         } else {
             return false;

File diff suppressed because it is too large
+ 253 - 228
main/inc/lib/events.lib.php


+ 11 - 12
main/inc/lib/exercise.lib.php

@@ -1187,7 +1187,6 @@ HTML;
                         </ul>
                     </div>
                 ';
-
                 if ($freeze) {
                     $relPath = api_get_path(WEB_CODE_PATH);
                     echo "
@@ -1203,6 +1202,7 @@ HTML;
                                 new ".($answerType == HOT_SPOT ? "HotspotQuestion" : "DelineationQuestion")."({
                                     questionId: $questionId,
                                     exerciseId: $exerciseId,
+                                    exeId: 0,
                                     selector: '#hotspot-preview-$questionId',
                                     for: 'preview',
                                     relPath: '$relPath'
@@ -2071,7 +2071,6 @@ HOTSPOT;
                     }
 
                     $results[$i]['exe_duration'] = !empty($results[$i]['exe_duration']) ? round($results[$i]['exe_duration'] / 60) : 0;
-
                     $user_list_id[] = $results[$i]['exe_user_id'];
                     $id = $results[$i]['exe_id'];
                     $dt = api_convert_and_format_date($results[$i]['exe_weighting']);
@@ -2221,16 +2220,16 @@ HOTSPOT;
                                 $filterByUser = isset($_GET['filter_by_user']) ? (int) $_GET['filter_by_user'] : 0;
                                 $delete_link = '<a href="exercise_report.php?'.api_get_cidreq().'&filter_by_user='.$filterByUser.'&filter='.$filter.'&exerciseId='.$exercise_id.'&delete=delete&did='.$id.'"
                                 onclick="javascript:if(!confirm(\''.sprintf(
-                                    get_lang('DeleteAttempt'),
+                                    addslashes(get_lang('DeleteAttempt')),
                                     $results[$i]['username'],
                                     $dt
                                 ).'\')) return false;">';
-                                $delete_link .=
-                                    Display:: return_icon(
-                                        'delete.png',
-                                        get_lang('Delete')
-                                    ).'</a>';
-                                $delete_link = utf8_encode($delete_link);
+                                $delete_link .= Display:: return_icon(
+                                    'delete.png',
+                                        addslashes(get_lang('Delete'))
+                                ).'</a>';
+
+                                //$delete_link = utf8_encode($delete_link);
 
                                 if (api_is_drh() && !api_is_platform_admin()) {
                                     $delete_link = null;
@@ -2528,9 +2527,9 @@ HOTSPOT;
             if ($hidePercentageSign) {
                 $percentageSign = '';
             }
-            $html = $percentage."$percentageSign  ($score / $weight)";
+            $html = $percentage."$percentageSign ($score / $weight)";
             if ($show_only_percentage) {
-                $html = $percentage."$percentageSign ";
+                $html = $percentage.$percentageSign;
             }
         } else {
             $html = $score.' / '.$weight;
@@ -2555,7 +2554,7 @@ HOTSPOT;
      */
     public static function getModelStyle($model, $percentage)
     {
-        $modelWithStyle = '<span class="'.$model['css_class'].'"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>';
+        $modelWithStyle = '<span class="'.$model['css_class'].'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>';
 
         return $modelWithStyle;
     }

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

@@ -1312,9 +1312,9 @@ function filter_extension(&$filename)
  * @param string $comment
  * @param int    $readonly
  * @param bool   $saveVisibility
- * @param int    $group_id  group.id
- * @param int    $sessionId Session ID, if any
- * @param int    $userId    creator user id
+ * @param int    $group_id         group.id
+ * @param int    $sessionId        Session ID, if any
+ * @param int    $userId           creator user id
  * @param bool   $sendNotification
  *
  * @return int id if inserted document
@@ -1599,6 +1599,7 @@ function search_img_from_html($html_file)
  * @param int    $visibility              (0 for invisible, 1 for visible, 2 for deleted)
  * @param bool   $generateNewNameIfExists
  * @param bool   $sendNotification        depends in conf setting "send_notification_when_document_added"
+ *
  * @return string actual directory name if it succeeds,
  *                boolean false otherwise
  */

+ 3 - 0
main/inc/lib/formvalidator/Element/DateRangePicker.php

@@ -172,6 +172,9 @@ class DateRangePicker extends HTML_QuickForm_text
                     $minDate
                     ranges: {
                          '".addslashes(get_lang('Today'))."': [moment(), moment()],
+                         '".addslashes(get_lang('Yesterday'))."': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],                                                  
+                         '".addslashes(get_lang('ThisMonth'))."': [moment().startOf('month'), moment().endOf('month')],
+                         '".addslashes(get_lang('LastMonth'))."': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')],                      
                          '".addslashes(get_lang('ThisWeek'))."': [moment().weekday(1), moment().weekday(5)],
                          '".addslashes(get_lang('NextWeek'))."': [moment().weekday(8), moment().weekday(12)]
                     },

+ 1 - 5
main/inc/lib/formvalidator/Element/DateTimePicker.php

@@ -68,11 +68,7 @@ class DateTimePicker extends HTML_QuickForm_text
     public function setValue($value)
     {
         $value = substr($value, 0, 16);
-        $this->updateAttributes(
-            [
-                'value' => $value,
-            ]
-        );
+        $this->updateAttributes(['value' => $value]);
     }
 
     /**

+ 23 - 23
main/inc/lib/formvalidator/FormValidator.class.php

@@ -178,10 +178,10 @@ EOT;
      * Adds a text field to the form.
      * A trim-filter is attached to the field.
      *
-     * @param string $label      The label for the form-element
-     * @param string $name       The element name
-     * @param bool   $required   (optional)    Is the form-element required (default=true)
-     * @param array  $attributes (optional)    List of attributes for the form-element
+     * @param string|array $label      The label for the form-element
+     * @param string       $name       The element name
+     * @param bool         $required   (optional)    Is the form-element required (default=true)
+     * @param array        $attributes (optional)    List of attributes for the form-element
      *
      * @return HTML_QuickForm_text
      */
@@ -925,15 +925,13 @@ EOT;
      * @param bool   $required (optional) Is the form-element required (default=true)
      * @param bool   $fullPage (optional) When it is true, the editor loads completed html code for a full page
      * @param array  $config   (optional) Configuration settings for the online editor
-     * @param bool   $style
      */
     public function addHtmlEditor(
         $name,
         $label,
         $required = true,
         $fullPage = false,
-        $config = [],
-        $style = false
+        $config = []
     ) {
         $attributes = [];
         $attributes['rows'] = isset($config['rows']) ? $config['rows'] : 15;
@@ -949,12 +947,11 @@ EOT;
 
         /** @var HtmlEditor $element */
         $element = $this->getElement($name);
-
-        if ($style) {
-            $config['style'] = true;
-        }
+        $config['style'] = false;
         if ($fullPage) {
             $config['fullPage'] = true;
+            // Adds editor.css in ckEditor
+            $config['style'] = true;
         }
 
         if ($element->editor) {
@@ -1774,18 +1771,7 @@ EOT;
                 );
             }).on('fileuploaddone', function (e, data) {
                 $.each(data.result.files, function (index, file) {
-                    if (file.url) {
-                        var link = $('<a>')
-                            .attr({target: '_blank', class : 'panel-image'})
-                            .prop('href', file.url);
-                        $(data.context.children()[index]).parent().wrap(link);
-                        // Update file name with new one from Chamilo
-                        $(data.context.children()[index]).parent().find('.file_name').html(file.name);
-                        var message = $('<div class=\"col-sm-3\">').html(
-                            $('<span class=\"message-image-success\"/>').text('".addslashes(get_lang('UplUploadSucceeded'))."')
-                        );
-                        $(data.context.children()[index]).parent().append(message);                    
-                    } else if (file.error) {
+                    if (file.error) {
                         var link = $('<div>')
                             .attr({class : 'panel-image'})                            ;
                         $(data.context.children()[index]).parent().wrap(link);
@@ -1795,7 +1781,21 @@ EOT;
                             $('<span class=\"message-image-danger\"/>').text(file.error)
                         );
                         $(data.context.children()[index]).parent().append(message);
+
+                        return;
                     }
+                    if (file.url) {
+                        var link = $('<a>')
+                            .attr({target: '_blank', class : 'panel-image'})
+                            .prop('href', file.url);
+                        $(data.context.children()[index]).parent().wrap(link);
+                    }
+                    // Update file name with new one from Chamilo
+                    $(data.context.children()[index]).parent().find('.file_name').html(file.name);
+                    var message = $('<div class=\"col-sm-3\">').html(
+                        $('<span class=\"message-image-success\"/>').text('".addslashes(get_lang('UplUploadSucceeded'))."')
+                    );
+                    $(data.context.children()[index]).parent().append(message);
                 });                
                 $('#dropzone').removeClass('hover');                
                 ".$redirectCondition."

+ 230 - 55
main/inc/lib/groupmanager.lib.php

@@ -56,6 +56,10 @@ class GroupManager
     const GROUP_TOOL_WIKI = 5;
     const GROUP_TOOL_CHAT = 6;
 
+    const DOCUMENT_MODE_SHARE = 0; // By default
+    const DOCUMENT_MODE_READ_ONLY = 1;
+    const DOCUMENT_MODE_COLLABORATION = 2;
+
     /**
      * GroupManager constructor.
      */
@@ -189,6 +193,18 @@ class GroupManager
         $category = self::get_category($category_id);
         $places = intval($places);
 
+        // Default values
+        $docState = self::TOOL_PRIVATE;
+        $calendarState = self::TOOL_PRIVATE;
+        $workState = self::TOOL_PRIVATE;
+        $anonuncementState = self::TOOL_PRIVATE;
+        $forumState = self::TOOL_PRIVATE;
+        $wikiState = self::TOOL_PRIVATE;
+        $chatState = self::TOOL_PRIVATE;
+        $selfRegAllowed = 0;
+        $selfUnregAllwoed = 0;
+        $documentAccess = 0;
+
         if ($category) {
             if ($places == 0) {
                 //if the amount of users per group is not filled in, use the setting from the category
@@ -207,16 +223,14 @@ class GroupManager
             $chatState = $category['chat_state'];
             $selfRegAllowed = $category['self_reg_allowed'];
             $selfUnregAllwoed = $category['self_unreg_allowed'];
-        } else {
-            $docState = self::TOOL_PRIVATE;
-            $calendarState = self::TOOL_PRIVATE;
-            $workState = self::TOOL_PRIVATE;
-            $anonuncementState = self::TOOL_PRIVATE;
-            $forumState = self::TOOL_PRIVATE;
-            $wikiState = self::TOOL_PRIVATE;
-            $chatState = self::TOOL_PRIVATE;
-            $selfRegAllowed = 0;
-            $selfUnregAllwoed = 0;
+            $documentAccess = isset($category['document_access']) ? $category['document_access'] : 0;
+        }
+
+        $allowDocumentAccess = api_get_configuration_value('group_document_access');
+        $documentCondition = '';
+        if ($allowDocumentAccess) {
+            $documentAccess = (int) $documentAccess;
+            $documentCondition = " document_access = $documentAccess, ";
         }
 
         $table_group = Database::get_course_table(TABLE_GROUP);
@@ -234,6 +248,7 @@ class GroupManager
                 chat_state = '".$chatState."',
                 self_registration_allowed = '".$selfRegAllowed."',
                 self_unregistration_allowed = '".$selfUnregAllwoed."',
+                $documentCondition
                 session_id='".intval($session_id)."'";
 
         Database::query($sql);
@@ -279,7 +294,6 @@ class GroupManager
                         'forum_category_title' => get_lang('GroupForums'),
                     ];
                     store_forumcategory($categoryParam);
-
                     $forum_categories = get_forum_categories();
                 }
 
@@ -331,7 +345,7 @@ class GroupManager
      */
     public static function create_subgroups($group_id, $number_of_groups)
     {
-        $course_id = api_get_course_int_id();
+        $courseId = api_get_course_int_id();
         $table_group = Database::get_course_table(TABLE_GROUP);
         $category_id = self::create_category(
             get_lang('Subgroups'),
@@ -368,7 +382,7 @@ class GroupManager
 
         foreach ($members as $group_id => $places) {
             $sql = "UPDATE $table_group SET max_student = $places
-                    WHERE c_id = $course_id  AND id = $group_id";
+                    WHERE c_id = $courseId  AND id = $group_id";
             Database::query($sql);
         }
     }
@@ -531,6 +545,7 @@ class GroupManager
                 self::get_subscribed_tutors($result)
             );
             $result['count_all'] = $result['count_users'] + $result['count_tutor'];
+            $result['document_access'] = isset($db_object->document_access) ? $db_object->document_access : self::DOCUMENT_MODE_SHARE;
         }
 
         return $result;
@@ -620,6 +635,7 @@ class GroupManager
      * @param bool Whether self registration is allowed or not
      * @param bool Whether self unregistration is allowed or not
      * @param int $categoryId
+     * @param int $documentAccess
      *
      * @return bool TRUE if properties are successfully changed, false otherwise
      */
@@ -635,15 +651,23 @@ class GroupManager
         $forum_state,
         $wiki_state,
         $chat_state,
-        $self_registration_allowed,
-        $self_unregistration_allowed,
-        $categoryId = null
+        $selfRegistrationAllowed,
+        $selfUnRegistrationAllowed,
+        $categoryId = null,
+        $documentAccess = 0
     ) {
         $table_group = Database::get_course_table(TABLE_GROUP);
         $table_forum = Database::get_course_table(TABLE_FORUM);
         $categoryId = intval($categoryId);
         $group_id = intval($group_id);
-        $course_id = api_get_course_int_id();
+        $courseId = api_get_course_int_id();
+
+        $allowDocumentAccess = api_get_configuration_value('group_document_access');
+        $documentCondition = '';
+        if ($allowDocumentAccess) {
+            $documentAccess = (int) $documentAccess;
+            $documentCondition = " document_access = $documentAccess, ";
+        }
 
         $sql = "UPDATE ".$table_group." SET
                     name='".Database::escape_string(trim($name))."',
@@ -656,10 +680,11 @@ class GroupManager
                     chat_state = '".Database::escape_string($chat_state)."',
                     description ='".Database::escape_string(trim($description))."',
                     max_student = '".Database::escape_string($maximum_number_of_students)."',
-                    self_registration_allowed = '".Database::escape_string($self_registration_allowed)."',
-                    self_unregistration_allowed = '".Database::escape_string($self_unregistration_allowed)."',
+                    self_registration_allowed = '".Database::escape_string($selfRegistrationAllowed)."',
+                    self_unregistration_allowed = '".Database::escape_string($selfUnRegistrationAllowed)."',
+                    $documentCondition
                     category_id = ".intval($categoryId)."
-                WHERE c_id = $course_id AND id=".$group_id;
+                WHERE c_id = $courseId AND id=".$group_id;
         $result = Database::query($sql);
 
         /* Here we are updating a field in the table forum_forum that perhaps
@@ -673,7 +698,7 @@ class GroupManager
         } elseif ($forum_state === 0) {
             $sql2 .= " forum_group_public_private='unavailable' ";
         }
-        $sql2 .= " WHERE c_id = $course_id AND forum_of_group=".$group_id;
+        $sql2 .= " WHERE c_id = $courseId AND forum_of_group=".$group_id;
         Database::query($sql2);
 
         return $result;
@@ -686,11 +711,11 @@ class GroupManager
      */
     public static function get_number_of_groups()
     {
-        $course_id = api_get_course_int_id();
+        $courseId = api_get_course_int_id();
         $table = Database::get_course_table(TABLE_GROUP);
         $sql = "SELECT COUNT(id) AS number_of_groups
                 FROM $table
-                WHERE c_id = $course_id ";
+                WHERE c_id = $courseId ";
         $res = Database::query($sql);
         $obj = Database::fetch_object($res);
 
@@ -707,10 +732,10 @@ class GroupManager
     public static function get_categories($course_code = null)
     {
         $course_info = api_get_course_info($course_code);
-        $course_id = $course_info['real_id'];
+        $courseId = $course_info['real_id'];
         $table = Database::get_course_table(TABLE_GROUP_CATEGORY);
         $sql = "SELECT * FROM $table
-                WHERE c_id = $course_id
+                WHERE c_id = $courseId
                 ORDER BY display_order";
         $res = Database::query($sql);
         $cats = [];
@@ -735,12 +760,12 @@ class GroupManager
             return [];
         }
 
-        $course_info = api_get_course_info($course_code);
-        $course_id = $course_info['real_id'];
+        $courseInfo = api_get_course_info($course_code);
+        $courseId = $courseInfo['real_id'];
         $id = intval($id);
         $table = Database::get_course_table(TABLE_GROUP_CATEGORY);
         $sql = "SELECT * FROM $table
-                WHERE c_id = $course_id AND id = $id
+                WHERE c_id = $courseId AND id = $id
                 LIMIT 1";
         $res = Database::query($sql);
 
@@ -764,11 +789,11 @@ class GroupManager
         }
 
         $course_info = api_get_course_info($course_code);
-        $course_id = $course_info['real_id'];
+        $courseId = $course_info['real_id'];
         $title = Database::escape_string($title);
         $table = Database::get_course_table(TABLE_GROUP_CATEGORY);
         $sql = "SELECT * FROM $table
-                WHERE c_id = $course_id AND title = '$title'
+                WHERE c_id = $courseId AND title = '$title'
                 LIMIT 1";
         $res = Database::query($sql);
         $category = [];
@@ -805,11 +830,11 @@ class GroupManager
             return false;
         }
 
-        $course_id = $course_info['real_id'];
+        $courseId = $course_info['real_id'];
         $sql = "SELECT gc.* FROM $table_group_cat gc, $table_group g
                 WHERE
-                    gc.c_id = $course_id AND
-                    g.c_id = $course_id AND
+                    gc.c_id = $courseId AND
+                    g.c_id = $courseId AND
                     gc.id = g.category_id AND 
                     g.iid = $group_id
                 LIMIT 1";
@@ -866,8 +891,8 @@ class GroupManager
     /**
      * Create group category.
      *
-     * @param string $title                       The title of the new category
-     * @param string $description                 The description of the new category
+     * @param string $title                      The title of the new category
+     * @param string $description                The description of the new category
      * @param int    $doc_state
      * @param int    $work_state
      * @param int    $calendar_state
@@ -875,10 +900,11 @@ class GroupManager
      * @param int    $forum_state
      * @param int    $wiki_state
      * @param int    $chat_state
-     * @param int    $self_registration_allowed
-     * @param int    $self_unregistration_allowed
+     * @param int    $selfRegistrationAllowed    allow users to self register
+     * @param int    $selfUnRegistrationAllowed  allow user to self unregister
      * @param int    $maximum_number_of_students
      * @param int    $groups_per_user
+     * @param int    $documentAccess             document access
      *
      * @return mixed
      */
@@ -892,10 +918,11 @@ class GroupManager
         $forum_state,
         $wiki_state,
         $chat_state = 1,
-        $self_registration_allowed = 0,
-        $self_unregistration_allowed = 0,
+        $selfRegistrationAllowed = 0,
+        $selfUnRegistrationAllowed = 0,
         $maximum_number_of_students = 8,
-        $groups_per_user = 0
+        $groups_per_user = 0,
+        $documentAccess = 0
     ) {
         if (empty($title)) {
             return false;
@@ -925,11 +952,16 @@ class GroupManager
             'wiki_state' => $wiki_state,
             'chat_state' => $chat_state,
             'groups_per_user' => $groups_per_user,
-            'self_reg_allowed' => $self_registration_allowed,
-            'self_unreg_allowed' => $self_unregistration_allowed,
+            'self_reg_allowed' => $selfRegistrationAllowed,
+            'self_unreg_allowed' => $selfUnRegistrationAllowed,
             'max_student' => $maximum_number_of_students,
         ];
 
+        $allowDocumentAccess = api_get_configuration_value('group_category_document_access');
+        if ($allowDocumentAccess) {
+            $params['document_access'] = $documentAccess;
+        }
+
         $categoryId = Database::insert($table, $params);
         if ($categoryId) {
             $sql = "UPDATE $table SET id = iid
@@ -955,10 +987,11 @@ class GroupManager
      * @param $forum_state
      * @param $wiki_state
      * @param $chat_state
-     * @param $self_registration_allowed
-     * @param $self_unregistration_allowed
+     * @param $selfRegistrationAllowed
+     * @param $selfUnRegistrationAllowed
      * @param $maximum_number_of_students
      * @param $groups_per_user
+     * @param $documentAccess
      */
     public static function update_category(
         $id,
@@ -971,15 +1004,23 @@ class GroupManager
         $forum_state,
         $wiki_state,
         $chat_state,
-        $self_registration_allowed,
-        $self_unregistration_allowed,
+        $selfRegistrationAllowed,
+        $selfUnRegistrationAllowed,
         $maximum_number_of_students,
-        $groups_per_user
+        $groups_per_user,
+        $documentAccess
     ) {
         $table = Database::get_course_table(TABLE_GROUP_CATEGORY);
         $id = intval($id);
 
-        $course_id = api_get_course_int_id();
+        $courseId = api_get_course_int_id();
+
+        $allowDocumentAccess = api_get_configuration_value('group_category_document_access');
+        $documentCondition = '';
+        if ($allowDocumentAccess) {
+            $documentAccess = (int) $documentAccess;
+            $documentCondition = " document_access = $documentAccess, ";
+        }
 
         $sql = "UPDATE ".$table." SET
                     title='".Database::escape_string($title)."',
@@ -992,10 +1033,11 @@ class GroupManager
                     wiki_state = '".Database::escape_string($wiki_state)."',
                     chat_state = '".Database::escape_string($chat_state)."',
                     groups_per_user   = '".Database::escape_string($groups_per_user)."',
-                    self_reg_allowed = '".Database::escape_string($self_registration_allowed)."',
-                    self_unreg_allowed = '".Database::escape_string($self_unregistration_allowed)."',
+                    self_reg_allowed = '".Database::escape_string($selfRegistrationAllowed)."',
+                    self_unreg_allowed = '".Database::escape_string($selfUnRegistrationAllowed)."',
+                    $documentCondition
                     max_student = ".intval($maximum_number_of_students)."
-                WHERE c_id = $course_id AND id = $id";
+                WHERE c_id = $courseId AND id = $id";
 
         Database::query($sql);
 
@@ -1016,9 +1058,10 @@ class GroupManager
                     $forum_state,
                     $wiki_state,
                     $chat_state,
-                    $self_registration_allowed,
-                    $self_unregistration_allowed,
-                    $id
+                    $selfRegistrationAllowed,
+                    $selfUnRegistrationAllowed,
+                    $id,
+                    $documentAccess
                 );
             }
         }
@@ -2252,6 +2295,10 @@ class GroupManager
 
                 $group_name = '<a class="'.$groupNameClass.'" href="group_space.php?'.api_get_cidreq(true, false).'&gidReq='.$this_group['id'].'">'.
                     Security::remove_XSS($this_group['name']).'</a> ';
+
+                $group_name2 = '<a href="suivi_group_space.php?cidReq='.api_get_course_id().'&gidReq='.$this_group['id'].'">
+                                '.get_lang('suivi_de').''.stripslashes($this_group['name']).'</a>';
+
                 if (!empty($user_id) && !empty($this_group['id_tutor']) && $user_id == $this_group['id_tutor']) {
                     $group_name .= Display::label(get_lang('OneMyGroups'), 'success');
                 } elseif ($isMember) {
@@ -2262,7 +2309,7 @@ class GroupManager
                     $group_name .= ' ('.$this_group['session_name'].')';
                 }
                 $group_name .= $session_img;
-                $row[] = $group_name.'<br />'.stripslashes(trim($this_group['description']));
+                $row[] = $group_name.$group_name2.'<br />'.stripslashes(trim($this_group['description']));
             } else {
                 if ($hideGroup === 'true') {
                     continue;
@@ -2955,4 +3002,132 @@ class GroupManager
     {
         self::setStatus($groupId, 0);
     }
+
+    /**
+     * @param int   $userId
+     * @param int   $courseId
+     * @param array $groupInfo
+     * @param array $documentInfoToBeCheck
+     * @param bool  $blockPage
+     *
+     * @return bool
+     */
+    public static function allowUploadEditDocument(
+        $userId,
+        $courseId,
+        $groupInfo,
+        $documentInfoToBeCheck = null,
+        $blockPage = false
+    ) {
+        // Admin and teachers can make any change no matter what
+        if (api_is_platform_admin() || api_is_allowed_to_edit()) {
+            return true;
+        }
+
+        if (empty($groupInfo) || !isset($groupInfo['id'])) {
+            if ($blockPage) {
+                api_not_allowed(true);
+            }
+
+            return false;
+        }
+
+        // Tutor can also make any change
+        $isTutor = GroupManager::is_tutor_of_group($userId, $groupInfo, $courseId);
+
+        if ($isTutor) {
+            return true;
+        }
+
+        // Just in case also check if document in group is available
+        if ($groupInfo['doc_state'] == 0) {
+            if ($blockPage) {
+                api_not_allowed(true);
+            }
+
+            return false;
+        }
+
+        // Default behaviour
+        $documentAccess = self::DOCUMENT_MODE_SHARE;
+
+        // Check category document access
+        /*$allowCategoryGroupDocumentAccess = api_get_configuration_value('group_category_document_access');
+        if ($allowCategoryGroupDocumentAccess) {
+            $category = GroupManager::get_category_from_group($groupInfo['iid']);
+            if (!empty($category) && isset($category['document_access'])) {
+                $documentAccess = (int) $category['document_access'];
+            }
+        }*/
+
+        // Check group document access
+        $allow = api_get_configuration_value('group_document_access');
+        if ($allow) {
+            if (isset($groupInfo['document_access'])) {
+                $documentAccess = (int) $groupInfo['document_access'];
+            }
+        }
+
+        // Check access for students
+        $result = false;
+        switch ($documentAccess) {
+            case self::DOCUMENT_MODE_SHARE:
+                // Default chamilo behaviour
+                // Student can upload his own content, cannot modify another content.
+                $isMember = GroupManager::is_subscribed($userId, $groupInfo);
+                if ($isMember) {
+                    // No document to check, allow access to document feature.
+                    if (empty($documentInfoToBeCheck)) {
+                        $result = true;
+                    } else {
+                        // Member can only edit his own document
+                        $authorId = isset($documentInfoToBeCheck['insert_user_id']) ? $documentInfoToBeCheck['insert_user_id'] : 0;
+                        // If "insert_user_id" is not set, check the author id from c_item_property
+                        if (empty($authorId) && isset($documentInfoToBeCheck['id'])) {
+                            $documentInfo = api_get_item_property_info(
+                                $courseId,
+                                'document',
+                                $documentInfoToBeCheck['id'],
+                                0
+                            );
+                            // Try to find this document in the session
+                            if (!empty($sessionId)) {
+                                $documentInfo = api_get_item_property_info(
+                                    $courseId,
+                                    'document',
+                                    $documentInfoToBeCheck['id'],
+                                    api_get_session_id()
+                                );
+                            }
+
+                            if (!empty($documentInfo) && isset($documentInfo['insert_user_id'])) {
+                                $authorId = $documentInfo['insert_user_id'];
+                            }
+                        }
+
+                        if ($authorId == $userId) {
+                            $result = true;
+                        }
+                    }
+                }
+                break;
+            case self::DOCUMENT_MODE_READ_ONLY:
+                // Student cannot upload content, cannot modify another content.
+                $result = false;
+                break;
+            case self::DOCUMENT_MODE_COLLABORATION:
+                // Student can upload content, can modify another content.
+                $isMember = GroupManager::is_subscribed($userId, $groupInfo);
+                if ($isMember) {
+                    $result = true;
+                }
+                break;
+        }
+
+        if ($blockPage && $result == false) {
+            api_not_allowed(true);
+        }
+
+        return $result;
+    }
 }

+ 1 - 2
main/inc/lib/javascript/ckeditor/config_js.php

@@ -4,13 +4,12 @@
 require_once __DIR__.'/../../../global.inc.php';
 
 $moreButtonsInMaximizedMode = false;
-
 if (api_get_setting('more_buttons_maximized_mode') === 'true') {
     $moreButtonsInMaximizedMode = true;
 }
 
 $template = new Template();
-$template->setCSSEditor();
+$template->setStyleMenuInCkEditor();
 $template->assign('moreButtonsInMaximizedMode', $moreButtonsInMaximizedMode);
 $courseId = api_get_course_int_id();
 $courseCondition = '';

+ 7 - 9
main/inc/lib/javascript/hotspot/js/hotspot.js

@@ -1154,7 +1154,8 @@ window.HotspotQuestion = (function () {
             case 'preview':
                 xhrQuestion = $.getJSON(config.relPath + 'exercise/hotspot_answers.as.php?' + _p.web_cid_query, {
                     modifyAnswers: parseInt(config.questionId),
-                    exe_id: parseInt(config.exerciseId)
+                    exerciseId: parseInt(config.exerciseId),
+                    exeId: parseInt(config.exeId)
                 });
                 break;
         }
@@ -1164,13 +1165,11 @@ window.HotspotQuestion = (function () {
                 case 'admin':
                     startHotspotsAdmin(questionInfo);
                     break;
-
                 case 'user':
                     startHotspotsUser(questionInfo);
                     break;
-
                 case 'solution':
-                //no break
+                    // no break
                 case 'preview':
                     startHotspotsSolution(questionInfo);
                     break;
@@ -1777,11 +1776,12 @@ window.DelineationQuestion = (function () {
                 });
                 break;
             case 'solution':
-            //no break
+                // no break
             case 'preview':
                 xhrQuestion = $.getJSON(config.relPath + 'exercise/hotspot_answers.as.php?' + _p.web_cid_query, {
                     modifyAnswers: parseInt(config.questionId),
-                    exe_id: parseInt(config.exerciseId)
+                    exerciseId: parseInt(config.exerciseId),
+                    exeId: parseInt(config.exeId)
                 });
                 break;
         }
@@ -1791,13 +1791,11 @@ window.DelineationQuestion = (function () {
                 case 'admin':
                     startAdminSvg(questionInfo);
                     break;
-
                 case 'user':
                     startUserSvg(questionInfo);
                     break;
-
                 case 'solution':
-                //no break
+                    // no break
                 case 'preview':
                     startPreviewSvg(questionInfo);
                     break;

Some files were not shown because too many files changed in this diff