ソースを参照

Merge branch '1.11.x' of https://github.com/chamilo/chamilo-lms into 1.11.x

Alex Aragón 6 年 前
コミット
ccb2cffa00
87 ファイル変更1350 行追加1865 行削除
  1. 4 0
      .github/ISSUE_TEMPLATE.md
  2. 2 0
      CONTRIBUTING.md
  3. 4 0
      app/Resources/public/css/base.css
  4. 19 14
      composer.json
  5. 9 0
      main/admin/user_import.php
  6. 1 109
      main/course_home/activity.php
  7. 6 9
      main/coursecopy/import_moodle.php
  8. 3 7
      main/exercise/TestCategory.php
  9. 2 2
      main/exercise/exercise.class.php
  10. 5 0
      main/exercise/exercise_submit.php
  11. 7 1
      main/exercise/overview.php
  12. 36 39
      main/exercise/question_pool.php
  13. BIN
      main/img/icons/22/scormbuilder.png
  14. BIN
      main/img/icons/32/agenda_na.png
  15. BIN
      main/img/icons/32/attendance_na.png
  16. BIN
      main/img/icons/32/backup.png
  17. BIN
      main/img/icons/32/blog_admin.png
  18. BIN
      main/img/icons/32/blog_na.png
  19. BIN
      main/img/icons/32/chat_na.png
  20. BIN
      main/img/icons/32/course_progress_na.png
  21. BIN
      main/img/icons/32/customcertificate.png
  22. BIN
      main/img/icons/32/customcertificate_na.png
  23. BIN
      main/img/icons/32/documents.png
  24. BIN
      main/img/icons/32/documents_na.png
  25. BIN
      main/img/icons/32/dropbox_na.png
  26. BIN
      main/img/icons/32/folder_document_na.png
  27. BIN
      main/img/icons/32/forum_na.png
  28. BIN
      main/img/icons/32/glossary_na.png
  29. BIN
      main/img/icons/32/gradebook_na.png
  30. BIN
      main/img/icons/32/info_na.png
  31. BIN
      main/img/icons/32/links_na.png
  32. BIN
      main/img/icons/32/members_na.png
  33. BIN
      main/img/icons/32/notebook_na.png
  34. BIN
      main/img/icons/32/survey_na.png
  35. BIN
      main/img/icons/32/valves_na.png
  36. BIN
      main/img/icons/32/wiki_na.png
  37. BIN
      main/img/icons/32/wiki_task_na.png
  38. BIN
      main/img/icons/32/works_na.png
  39. 0 1
      main/inc/ajax/model.ajax.php
  40. 6 1
      main/inc/lib/AnnouncementManager.php
  41. 466 418
      main/inc/lib/MoodleImport.php
  42. 254 233
      main/inc/lib/course_home.lib.php
  43. 0 1
      main/inc/lib/document.lib.php
  44. 4 2
      main/inc/lib/exercise.lib.php
  45. 3 1
      main/inc/lib/exercise_show_functions.lib.php
  46. 1 0
      main/inc/lib/fixlinks.js
  47. 100 10
      main/inc/lib/formvalidator/Element/DateTimeRangePicker.php
  48. 0 86
      main/inc/lib/formvalidator/FormValidator.class.php
  49. 5 6
      main/inc/lib/template.lib.php
  50. 16 7
      main/inc/lib/zombie/zombie_manager.class.php
  51. 45 75
      main/inc/lib/zombie/zombie_report.class.php
  52. 19 1
      main/install/configuration.dist.php
  53. 0 2
      main/lp/learnpath.class.php
  54. 1 0
      main/lp/scorm_api.php
  55. 6 2
      main/mySpace/exercise_category_report.php
  56. 1 1
      main/mySpace/lp_tracking.php
  57. 11 0
      main/session/session_import.php
  58. 10 9
      main/social/invitations.php
  59. 0 1
      main/survey/ch_yesno.php
  60. 4 4
      main/survey/create_meeting.php
  61. 3 4
      main/survey/create_new_survey.php
  62. 36 44
      main/survey/fillsurvey.php
  63. 56 14
      main/survey/meeting.php
  64. 21 32
      main/survey/preview.php
  65. 57 9
      main/survey/survey.lib.php
  66. 7 4
      main/survey/survey.php
  67. 25 14
      main/survey/survey_question.php
  68. 1 1
      main/template/default/course_home/activity.tpl
  69. 7 7
      main/template/default/layout/main.js.tpl
  70. 12 13
      main/template/default/layout/show_header.tpl
  71. 7 3
      main/tracking/courseLog.php
  72. 0 2
      plugin/courseblock/CourseBlockPlugin.php
  73. 0 3
      plugin/dictionary/terms.php
  74. 1 1
      plugin/ims_lti/ImsLtiPlugin.php
  75. 0 621
      plugin/ims_lti/OAuthSimple.php
  76. 7 1
      plugin/ims_lti/README.md
  77. 0 1
      plugin/ims_lti/form.php
  78. 7 19
      plugin/ims_lti/item_return.php
  79. 0 1
      plugin/ims_lti/outcome_service.php
  80. BIN
      plugin/notebookteacher/resources/img/32/notebookteacher_na.png
  81. 17 12
      plugin/notebookteacher/src/NotebookTeacherPlugin.php
  82. BIN
      plugin/test2pdf/resources/img/32/test2pdf.png
  83. BIN
      plugin/test2pdf/resources/img/32/test2pdf_na.png
  84. 22 13
      plugin/test2pdf/src/test2pdf_plugin.class.php
  85. 3 1
      src/Chamilo/CourseBundle/Component/CourseCopy/CourseBuilder.php
  86. 4 2
      src/Chamilo/CourseBundle/Component/CourseCopy/CourseRestorer.php
  87. 7 1
      src/Chamilo/CourseBundle/Component/CourseCopy/Resources/Survey.php

+ 4 - 0
.github/ISSUE_TEMPLATE.md

@@ -1,3 +1,7 @@
+Before reporting, make sure you check our [official documentation](https://docs.chamilo.org).
+
+If you are fairly sure you found an issue, please use the following template (remove things in other languages than your own and remove these 2 first lines).
+
 ### Current behavior / Resultado actual / Résultat actuel
 
 ### Expected behavior / Resultado esperado / Résultat attendu

+ 2 - 0
CONTRIBUTING.md

@@ -7,6 +7,8 @@ Chamilo LMS is an LAMP-based e-learning web platform that focuses on providing a
 environment for teachers to improve both the quality and availability of their
 educational material, and on providing students with an awesome learning platform.
 
+Before you report an issue, please check the [official Chamilo documentation](https://docs.chamilo.org) (not always up to date)
+
 We are a relatively small development team and greatly welcome any contribution
 from the outside world, although we will thoroughly review them before integration,
 to make sure they do not introduce security vulnerabilities or degrade the ease

+ 4 - 0
app/Resources/public/css/base.css

@@ -7727,6 +7727,10 @@ ul#toolnavbox li a.icons-text {
     width: 200px;
 }
 
+ul#toolnavbox li a.icons-text img {
+    margin-left: 5px;
+}
+
 ul#toolnavbox li a.text {
     width: 180px;
 }

+ 19 - 14
composer.json

@@ -47,26 +47,30 @@
         "doctrine/orm": "~2.4",
         "doctrine/dbal": "~2.4",
         "doctrine/migrations": "~1.0@dev",
+        "symfony/console": "~2.3",
+        "symfony/yaml": "~2.3",
         "symfony/doctrine-bridge": "~2.8",
         "symfony/validator": "~2.8",
         "symfony/filesystem": "~2.8",
+        "symfony/http-foundation": "~2.8",
         "symfony/security": "~2.8",
         "symfony/serializer": "~3.0",
         "doctrine/data-fixtures": "~1.0@dev",
         "knplabs/gaufrette": "~0.3",
-        "gedmo/doctrine-extensions": "~2.3",
-        "sonata-project/user-bundle": "~3.0",
-        "toin0u/digitalocean": "dev-master",
-
+        "friendsofsymfony/user-bundle": "~1.3",
+        "sonata-project/admin-bundle": "~3.1",
+        "sonata-project/core-bundle": "~3.1",
+        "sonata-project/user-bundle": "3.3.0",
+        "toin0u/digitalocean": "1.4.2",
         "michelf/php-markdown": "~1.7",
         "sabre/vobject": "~3.1",
 
         "emojione/emojione": "1.3.0",
 
-        "zendframework/zend-config": "2.5.1|~2.6|^3.0",
-        "zendframework/zend-feed": "2.5.1|~2.6|^3.0",
-        "zendframework/zend-http": "2.5.1|~2.6|^3.0",
-        "zendframework/zend-soap": "2.5.1|~2.6|^3.0",
+        "zendframework/zend-config": "~2.6|^3.0",
+        "zendframework/zend-feed": "~2.6|^3.0",
+        "zendframework/zend-http": "~2.6|^3.0",
+        "zendframework/zend-soap": "~2.6|^3.0",
         "robrichards/xmlseclibs": "3.0.*",
 
         "ezyang/htmlpurifier": "4.9.*",
@@ -91,19 +95,20 @@
         "media-alchemyst/media-alchemyst": "~0.5",
         "php-ffmpeg/php-ffmpeg": "0.5.1",
         "imagine/imagine": "0.6.3",
-        "jeroendesloovere/vcard": "^1.2",
+        "jeroendesloovere/vcard": "~1.7",
         "knplabs/doctrine-behaviors": "~1.1",
-        "jimmiw/php-time-ago": "^0.4.14",
-        "phpoffice/phpword": "^0.14",
+        "jimmiw/php-time-ago": "0.4.15",
+        "phpoffice/phpword": "~0.14",
         "facebook/php-sdk-v4": "~5.0",
         "kigkonsult/icalcreator": "2.24",
         "essence/essence": "2.6.1",
-        "chamilo/pclzip": "^2.8",
+        "chamilo/pclzip": "~2.8",
         "chamilo/chash": "0.1.1",
         "paragonie/random-lib": "2.0.0",
         "ocramius/proxy-manager": "~1.0|2.0.*",
-        "culqi/culqi-php": "v1.3.3",
-        "knplabs/knp-components": "^1.3"
+        "culqi/culqi-php": "1.3.4",
+        "knplabs/knp-components": "~1.3",
+        "guzzlehttp/guzzle": "~6.0"
     },
     "require-dev": {
         "behat/behat": "@stable",

+ 9 - 0
main/admin/user_import.php

@@ -1,5 +1,6 @@
 <?php
 /* For licensing terms, see /license.txt */
+
 /**
  * This tool allows platform admins to add users by uploading a CSV or XML file.
  *
@@ -548,6 +549,14 @@ $form->addButtonImport(get_lang('Import'));
 $defaults['formSent'] = 1;
 $defaults['sendMail'] = 0;
 $defaults['file_type'] = 'csv';
+
+$extraSettings = api_get_configuration_value('user_import_settings');
+if (!empty($extraSettings) && isset($extraSettings['options']) &&
+    isset($extraSettings['options']['send_mail_default_option'])
+) {
+    $defaults['sendMail'] = $extraSettings['options']['send_mail_default_option'];
+}
+
 $form->setDefaults($defaults);
 $form->display();
 

+ 1 - 109
main/course_home/activity.php

@@ -42,8 +42,6 @@ if ($enabled === 'true') {
     $pluginExtra = $plugin->getTeacherLink();
 }
 
-//	COURSE ADMIN ONLY VIEW
-$blocks = [];
 // Start of tools for CourseAdmins (teachers/tutors)
 if ($session_id === 0 && api_is_course_admin() && api_is_allowed_to_edit(null, true)) {
     $content .= '<div class="alert alert-success" style="border:0px; margin-top: 0px;padding:0px;">
@@ -53,46 +51,7 @@ if ($session_id === 0 && api_is_course_admin() && api_is_allowed_to_edit(null, t
     $content .= '</div>
 		<div class="alert alert-success" id="id_confirmation_message" style="display:none"></div>
 	</div>';
-
     $content .= $pluginExtra;
-
-    if (api_get_setting('show_session_data') == 'true' && $session_id > 0) {
-        $content .= '
-        <div class="row">
-            <div class="col-xs-12 col-md-12">
-                <span class="viewcaption">'.get_lang('SessionData').'</span>
-                <table class="course_activity_home">'.
-                    CourseHome::show_session_data($session_id).'
-                </table>
-            </div>
-        </div>';
-    }
-
-    $my_list = CourseHome::get_tools_category(TOOL_AUTHORING);
-
-    $blocks[] = [
-        'title' => get_lang('Authoring'),
-        'class' => 'course-tools-author',
-        'content' => CourseHome::show_tools_category($my_list),
-    ];
-
-    $list1 = CourseHome::get_tools_category(TOOL_INTERACTION);
-    $list2 = CourseHome::get_tools_category(TOOL_COURSE_PLUGIN);
-    $my_list = array_merge($list1, $list2);
-
-    $blocks[] = [
-        'title' => get_lang('Interaction'),
-        'class' => 'course-tools-interaction',
-        'content' => CourseHome::show_tools_category($my_list),
-    ];
-
-    $my_list = CourseHome::get_tools_category(TOOL_ADMIN_PLATFORM);
-
-    $blocks[] = [
-        'title' => get_lang('Administration'),
-        'class' => 'course-tools-administration',
-        'content' => CourseHome::show_tools_category($my_list),
-    ];
 } elseif (api_is_coach()) {
     $content .= $pluginExtra;
     if (api_get_setting('show_session_data') === 'true' && $session_id > 0) {
@@ -103,76 +62,9 @@ if ($session_id === 0 && api_is_course_admin() && api_is_allowed_to_edit(null, t
         $content .= CourseHome::show_session_data($session_id);
         $content .= '</table></div></div>';
     }
-
-    $my_list = CourseHome::get_tools_category(TOOL_STUDENT_VIEW);
-
-    $blocks[] = [
-        'content' => CourseHome::show_tools_category($my_list),
-    ];
-
-    $sessionsCopy = api_get_setting('allow_session_course_copy_for_teachers');
-    if ($sessionsCopy === 'true') {
-        // Adding only maintenance for coaches.
-        $myList = CourseHome::get_tools_category(TOOL_ADMIN_PLATFORM);
-        $onlyMaintenanceList = [];
-
-        foreach ($myList as $item) {
-            if ($item['name'] === 'course_maintenance') {
-                $item['link'] = 'course_info/maintenance_coach.php';
-
-                $onlyMaintenanceList[] = $item;
-            }
-        }
-
-        $blocks[] = [
-            'title' => get_lang('Administration'),
-            'content' => CourseHome::show_tools_category($onlyMaintenanceList),
-        ];
-    }
-} else {
-    $tools = CourseHome::get_tools_category(TOOL_STUDENT_VIEW);
-    $isDrhOfCourse = CourseManager::isUserSubscribedInCourseAsDrh(
-        api_get_user_id(),
-        api_get_course_info()
-    );
-
-    // Force user icon for DRH
-    if ($isDrhOfCourse) {
-        $addUserTool = true;
-        foreach ($tools as $tool) {
-            if ($tool['name'] === 'user') {
-                $addUserTool = false;
-                break;
-            }
-        }
-
-        if ($addUserTool) {
-            $tools[] = [
-                'c_id' => api_get_course_int_id(),
-                'name' => 'user',
-                'link' => 'user/user.php',
-                'image' => 'members.gif',
-                'visibility' => '1',
-                'admin' => '0',
-                'address' => 'squaregrey.gif',
-                'added_tool' => '0',
-                'target' => '_self',
-                'category' => 'interaction',
-                'session_id' => api_get_session_id(),
-            ];
-        }
-    }
-
-    if (count($tools) > 0) {
-        $blocks[] = ['content' => CourseHome::show_tools_category($tools)];
-    }
-
-    if ($isDrhOfCourse) {
-        $drhTool = CourseHome::get_tools_category(TOOL_DRH);
-        $blocks[] = ['content' => CourseHome::show_tools_category($drhTool)];
-    }
 }
 
+$blocks = CourseHome::getUserBlocks();
 $activityView = new Template('', false, false, false, false, false, false);
 $activityView->assign('blocks', $blocks);
 

+ 6 - 9
main/coursecopy/import_moodle.php

@@ -35,21 +35,18 @@ $form->addButtonImport(get_lang('Import'));
 
 if ($form->validate()) {
     $file = $_FILES['moodle_file'];
+
     $moodleImport = new MoodleImport();
-    $responseImport = $moodleImport->import($file);
 
-    Display::cleanFlashMessages();
+    try {
+        $responseImport = $moodleImport->import($file);
 
-    if ($responseImport) {
         Display::addFlash(
-            Display::return_message(
-                get_lang('MoodleFileImportedSuccessfully'),
-                'success'
-            )
+            Display::return_message(get_lang('MoodleFileImportedSuccessfully'), 'success')
         );
-    } else {
+    } catch (Exception $exception) {
         Display::addFlash(
-            Display::return_message(get_lang('ErrorImportingFile'), 'error')
+            Display::return_message($exception->getMessage(), 'error')
         );
     }
 }

+ 3 - 7
main/exercise/TestCategory.php

@@ -305,10 +305,8 @@ class TestCategory
      *
      * @return string
      */
-    public static function getCategoryNameForQuestion(
-        $questionId,
-        $courseId = 0
-    ) {
+    public static function getCategoryNameForQuestion($questionId, $courseId = 0)
+    {
         if (empty($courseId)) {
             $courseId = api_get_course_int_id();
         }
@@ -399,9 +397,7 @@ class TestCategory
     public static function getListOfCategoriesNameForTest($exerciseId, $grouped_by_category = true)
     {
         $result = [];
-        $categories = self::getListOfCategoriesIDForTest(
-            $exerciseId
-        );
+        $categories = self::getListOfCategoriesIDForTest($exerciseId);
 
         foreach ($categories as $catInfo) {
             $categoryId = $catInfo['id'];

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

@@ -7510,9 +7510,9 @@ class Exercise
                 INNER JOIN $TBL_QUESTIONS q
                 ON (e.question_id = q.id AND e.c_id = q.c_id)
                 INNER JOIN $categoryRelTable catRel
-                ON (catRel.question_id = e.question_id)
+                ON (catRel.question_id = e.question_id AND catRel.c_id = e.c_id)
                 INNER JOIN $categoryTable cat
-                ON (cat.id = catRel.category_id)
+                ON (cat.id = catRel.category_id AND cat.c_id = e.c_id)
                 WHERE
                   e.c_id = {$this->course_id} AND
                   e.exercice_id	= ".intval($this->id);

+ 5 - 0
main/exercise/exercise_submit.php

@@ -331,6 +331,11 @@ $questionListUncompressed = $objExercise->getQuestionListWithMediasUncompressed(
 Session::write('question_list_uncompressed', $questionListUncompressed);
 $clock_expired_time = null;
 if (empty($exercise_stat_info)) {
+    $disable = api_get_configuration_value('exercises_disable_new_attempts');
+    if ($disable) {
+        api_not_allowed(true);
+    }
+
     if ($debug) {
         error_log('5  $exercise_stat_info is empty ');
     }

+ 7 - 1
main/exercise/overview.php

@@ -106,7 +106,7 @@ if (api_get_configuration_value('save_titles_as_html')) {
     );
 }
 
-//Exercise description
+// Exercise description
 if (!empty($objExercise->description)) {
     $html .= Display::div($objExercise->description, ['class' => 'exercise_description']);
 }
@@ -345,6 +345,12 @@ if ($time_control) {
 
 $html .= $message;
 
+$disable = api_get_configuration_value('exercises_disable_new_attempts');
+
+if ($disable && empty($exercise_stat_info)) {
+    $exercise_url_button = Display::return_message(get_lang('NewExerciseAttemptDisabled'));
+}
+
 if (!empty($exercise_url_button)) {
     $html .= Display::div(
         Display::div(

+ 36 - 39
main/exercise/question_pool.php

@@ -432,7 +432,10 @@ if ($exerciseId > 0) {
     $from = '';
     if (isset($courseCategoryId) && $courseCategoryId > 0) {
         $from = ", $TBL_COURSE_REL_CATEGORY crc ";
-        $where .= " AND crc.c_id=$selected_course AND crc.question_id=qu.id AND crc.category_id=$courseCategoryId";
+        $where .= " AND 
+                crc.c_id = $selected_course AND 
+                crc.question_id = qu.id AND 
+                crc.category_id = $courseCategoryId";
     }
     if (isset($exerciseLevel) && $exerciseLevel != -1) {
         $where .= ' AND level='.$exerciseLevel;
@@ -466,7 +469,8 @@ if ($exerciseId > 0) {
     $level_where = '';
     $from = '';
     if (isset($courseCategoryId) && $courseCategoryId > 0) {
-        $from = " INNER JOIN  $TBL_COURSE_REL_CATEGORY crc ON crc.question_id=q.id AND crc.c_id= q.c_id ";
+        $from = " INNER JOIN $TBL_COURSE_REL_CATEGORY crc 
+                  ON crc.question_id = q.id AND crc.c_id = q.c_id ";
         $level_where .= " AND
                 crc.c_id = $selected_course AND
                 crc.category_id = $courseCategoryId";
@@ -485,12 +489,13 @@ if ($exerciseId > 0) {
                 INNER JOIN $TBL_EXERCISE_QUESTION r
                 ON (q.c_id = r.c_id AND q.id = r.question_id)
                 INNER JOIN $TBL_EXERCISES ex
-                ON (ex.id = r.exercice_id AND ex.c_id = r.c_id )
+                ON (ex.id = r.exercice_id AND ex.c_id = r.c_id)
                 $from
                 WHERE
                     ex.c_id = '$selected_course' AND
                     ex.active = '-1'
-                    $level_where $answer_where
+                    $level_where 
+                    $answer_where
              )
              UNION
              (
@@ -501,7 +506,8 @@ if ($exerciseId > 0) {
                 WHERE
                     q.c_id = '$selected_course' AND
                     r.question_id is null
-                    $level_where $answer_where
+                    $level_where 
+                    $answer_where
              )
              UNION
              (
@@ -512,7 +518,8 @@ if ($exerciseId > 0) {
                 WHERE
                     r.c_id = '$selected_course' AND
                     (r.exercice_id = '-1' OR r.exercice_id = '0')
-                    $level_where $answer_where
+                    $level_where 
+                    $answer_where
              )";
     $result = Database::query($sql);
     while ($row = Database::fetch_array($result, 'ASSOC')) {
@@ -667,14 +674,14 @@ if ($fromExercise <= 0) {
     // NOT IN A TEST - IN THE COURSE
     if ($selected_course == api_get_course_int_id()) {
         $actionLabel = get_lang('Modify');
-        $actionIcon1 = "edit";
-        $actionIcon2 = "delete";
+        $actionIcon1 = 'edit';
+        $actionIcon2 = 'delete';
         // We are in the course, question title can be a link to the question edit page
         $questionTagA = 1;
     } else { // NOT IN A TEST - NOT IN THE COURSE
         $actionLabel = get_lang('Reuse');
         $actionIcon1 = get_lang('MustBeInATest');
-        $actionIcon2 = "";
+        $actionIcon2 = '';
         // We are not in this course, to messy if we link to the question in another course
         $questionTagA = 0;
     }
@@ -682,51 +689,52 @@ if ($fromExercise <= 0) {
     // IN A TEST - IN THE COURSE
     if ($selected_course == api_get_course_int_id()) {
         $actionLabel = get_lang('Reuse');
-        $actionIcon1 = "add";
-        $actionIcon2 = "";
+        $actionIcon1 = 'add';
+        $actionIcon2 = '';
         $questionTagA = 1;
     } else {
         // IN A TEST - NOT IN THE COURSE
         $actionLabel = get_lang('Reuse');
-        $actionIcon1 = "clone";
-        $actionIcon2 = "";
+        $actionIcon1 = 'clone';
+        $actionIcon2 = '';
         $questionTagA = 0;
     }
 }
+
 // Display table
 $header = [
     [
         get_lang('QuestionUpperCaseFirstLetter'),
         false,
-        ["style" => "text-align:center"],
+        ['style' => 'text-align:center'],
         '',
     ],
     [
         get_lang('Type'),
         false,
-        ["style" => "text-align:center"],
-        ["style" => "text-align:center"],
+        ['style' => 'text-align:center'],
+        ['style' => 'text-align:center'],
         '',
     ],
     [
         get_lang('QuestionCategory'),
         false,
-        ["style" => "text-align:center"],
-        ["style" => "text-align:center"],
+        ['style' => 'text-align:center'],
+        ['style' => 'text-align:center'],
         '',
     ],
     [
         get_lang('Difficulty'),
         false,
-        ["style" => "text-align:center"],
-        ["style" => "text-align:center"],
+        ['style' => 'text-align:center'],
+        ['style' => 'text-align:center'],
         '',
     ],
     [
         $actionLabel,
         false,
-        ["style" => "text-align:center"],
-        ["style" => "text-align:center"],
+        ['style' => 'text-align:center'],
+        ['style' => 'text-align:center'],
         '',
     ],
 ];
@@ -735,20 +743,9 @@ $data = [];
 
 if (is_array($mainQuestionList)) {
     foreach ($mainQuestionList as $question) {
-        /*if ($hideDoubles) {
-            if (in_array($question['question'], $questionAdded)) {
-                continue;
-            }
-        }
-        $questionAdded[$question['question']] = $question;*/
-
         $row = [];
-
         // This function checks if the question can be read
-        $question_type = get_question_type_for_question(
-            $selected_course,
-            $question['id']
-        );
+        $question_type = get_question_type_for_question($selected_course, $question['id']);
 
         if (empty($question_type)) {
             continue;
@@ -779,7 +776,7 @@ if (is_array($mainQuestionList)) {
             $answerType,
             $session_id,
             $exerciseId
-        )."&nbsp;".
+        ).'&nbsp;'.
         get_action_icon_for_question(
             $actionIcon2,
             $fromExercise,
@@ -900,7 +897,6 @@ function get_action_icon_for_question(
     $in_session_id,
     $in_exercise_id
 ) {
-    $res = "";
     $getParams = "&selected_course=$in_selected_course&courseCategoryId=$in_courseCategoryId&exerciseId=$in_exercise_id&exerciseLevel=$in_exerciseLevel&answerType=$in_answerType&session_id=$in_session_id";
     switch ($in_action) {
         case 'delete':
@@ -931,7 +927,8 @@ function get_action_icon_for_question(
             unset($myObjEx);
             break;
         case 'clone':
-            $url = api_get_self()."?".api_get_cidreq().$getParams."&question_copy=$in_questionid&course_id=$in_selected_course&fromExercise=$from_exercise";
+            $url = api_get_self()."?".api_get_cidreq().$getParams.
+                "&question_copy=$in_questionid&course_id=$in_selected_course&fromExercise=$from_exercise";
             $res = Display::url(
                 Display::return_icon('cd.png', get_lang('ReUseACopyInCurrentTest')),
                 $url
@@ -968,9 +965,9 @@ function get_question_type_for_question($in_selectedcourse, $in_questionid)
  *
  * @author hubert.borderiou 13-10-2011
  */
-function get_question_categorie_for_question($in_courseid, $in_questionid)
+function get_question_categorie_for_question($courseId, $questionId)
 {
-    $cat = TestCategory::getCategoryNameForQuestion($in_questionid, $in_courseid);
+    $cat = TestCategory::getCategoryNameForQuestion($questionId, $courseId);
 
     return $cat;
 }

BIN
main/img/icons/22/scormbuilder.png


BIN
main/img/icons/32/agenda_na.png


BIN
main/img/icons/32/attendance_na.png


BIN
main/img/icons/32/backup.png


BIN
main/img/icons/32/blog_admin.png


BIN
main/img/icons/32/blog_na.png


BIN
main/img/icons/32/chat_na.png


BIN
main/img/icons/32/course_progress_na.png


BIN
main/img/icons/32/customcertificate.png


BIN
main/img/icons/32/customcertificate_na.png


BIN
main/img/icons/32/documents.png


BIN
main/img/icons/32/documents_na.png


BIN
main/img/icons/32/dropbox_na.png


BIN
main/img/icons/32/folder_document_na.png


BIN
main/img/icons/32/forum_na.png


BIN
main/img/icons/32/glossary_na.png


BIN
main/img/icons/32/gradebook_na.png


BIN
main/img/icons/32/info_na.png


BIN
main/img/icons/32/links_na.png


BIN
main/img/icons/32/members_na.png


BIN
main/img/icons/32/notebook_na.png


BIN
main/img/icons/32/survey_na.png


BIN
main/img/icons/32/valves_na.png


BIN
main/img/icons/32/wiki_na.png


BIN
main/img/icons/32/wiki_task_na.png


BIN
main/img/icons/32/works_na.png


+ 0 - 1
main/inc/ajax/model.ajax.php

@@ -1332,7 +1332,6 @@ switch ($action) {
             $overwriteColumnHeaderExport['only_score'] = get_lang('Score').' - '.get_lang('ScoreNote');
             $overwriteColumnHeaderExport['total'] = get_lang('Score').' - '.get_lang('ScoreTest');
         }
-
         $categoryList = TestCategory::getListOfCategoriesIDForTest($exerciseId, $courseId);
 
         if (!empty($categoryList)) {

+ 6 - 1
main/inc/lib/AnnouncementManager.php

@@ -1624,6 +1624,11 @@ class AnnouncementManager
 
         $allowDrhAccess = api_get_configuration_value('allow_drh_access_announcement');
 
+        if ($allowDrhAccess && api_is_drh()) {
+            // DRH only can see visible
+            $searchCondition .= ' AND (ip.visibility = 1)';
+        }
+
         if (api_is_allowed_to_edit(false, true) ||
             ($allowUserEditSetting && !api_is_anonymous()) ||
             ($allowDrhAccess && api_is_drh())
@@ -1819,7 +1824,7 @@ class AnnouncementManager
                                 $searchCondition  AND
                                 ip.visibility='1' AND
                                 announcement.session_id IN ( 0,".api_get_session_id().")
-                            $groupBy
+                                $groupBy
                             ";
                 }
             }

ファイルの差分が大きいため隠しています
+ 466 - 418
main/inc/lib/MoodleImport.php


+ 254 - 233
main/inc/lib/course_home.lib.php

@@ -132,7 +132,7 @@ class CourseHome
         }
 
         foreach ($all_tools as &$tool) {
-            if ($tool['image'] == 'scormbuilder.gif') {
+            if (isset($tool['image']) && $tool['image'] == 'scormbuilder.gif') {
                 // check if the published learnpath is visible for student
                 $lpId = self::getPublishedLpIdFromLink($tool['link']);
 
@@ -175,6 +175,7 @@ class CourseHome
 
             // Setting the actual image url
             $tool['img'] = Display::returnIconPath($tool['img']);
+            $target = isset($tool['target']) ? $tool['target'] : '';
 
             // VISIBLE
             if (($tool['visibility'] ||
@@ -190,7 +191,7 @@ class CourseHome
                     $cell_content .= '<a href="javascript: void(0);" onclick="javascript: window.open(\''.$tool['link'].$link_annex.'\',\'window_chat'.api_get_course_id().'\',config=\'height=\'+600+\', width=\'+825+\', left=2, top=2, toolbar=no, menubar=no, scrollbars=yes, resizable=yes, location=no, directories=no, status=no\')" target="'.$tool['target'].'"><img src="'.$tool['img'].'" title="'.$tool_name.'" alt="'.$tool_name.'" align="absmiddle" border="0">'.$tool_name.'</a>';
                 } else {
                     // don't replace img with display::return_icon because $tool['img'] = api_get_path(WEB_IMG_PATH).$tool['img']
-                    $cell_content .= '<a href="'.$tool['link'].$link_annex.'" target="'.$tool['target'].'"><img src="'.$tool['img'].'" title="'.$tool_name.'" alt="'.$tool_name.'" align="absmiddle" border="0">'.$tool_name.'</a>';
+                    $cell_content .= '<a href="'.$tool['link'].$link_annex.'" target="'.$target.'"><img src="'.$tool['img'].'" title="'.$tool_name.'" alt="'.$tool_name.'" align="absmiddle" border="0">'.$tool_name.'</a>';
                 }
             } else {
                 // INVISIBLE
@@ -340,8 +341,8 @@ class CourseHome
                 $all_tools_list[] = $properties;
             }
         }
+        $lnk = [];
         if (isset($all_tools_list)) {
-            $lnk = [];
             foreach ($all_tools_list as &$tool) {
                 if ($tool['image'] == 'scormbuilder.gif') {
                     // check if the published learnpath is visible for student
@@ -390,7 +391,9 @@ class CourseHome
                 } elseif (strpos($tool['name'], 'chat') !== false && api_get_course_setting('allow_open_chat_window')) {
                     $html .= '<a href="javascript: void(0);" onclick="javascript: window.open(\''.htmlspecialchars($tool['link']).$qm_or_amp.api_get_cidreq().'\',\'window_chat'.api_get_course_id().'\',config=\'height=\'+600+\', width=\'+825+\', left=2, top=2, toolbar=no, menubar=no, scrollbars=yes, resizable=yes, location=no, directories=no, status=no\')" target="'.$tool['target'].'" '.$class.'>';
                 } else {
-                    $html .= '<a href="'.htmlspecialchars($tool['link']).(($tool['image'] == 'external.gif' || $tool['image'] == 'external_na.gif') ? '' : $qm_or_amp.api_get_cidreq()).'" target="'.$tool['target'].'" '.$class.'>';
+                    $target = isset($tool['target']) ? $tool['target'] : '';
+                    $html .= '<a href="'.
+                        htmlspecialchars($tool['link']).(($tool['image'] == 'external.gif' || $tool['image'] == 'external_na.gif') ? '' : $qm_or_amp.api_get_cidreq()).'" target="'.$target.'" '.$class.'>';
                 }
 
                 $tool_name = self::translate_tool_name($tool);
@@ -405,7 +408,7 @@ class CourseHome
 
                 // This part displays the links to hide or remove a tool.
                 // These links are only visible by the course manager.
-                unset($lnk);
+                $lnk = [];
                 if (api_is_allowed_to_edit(null, true) && !api_is_coach()) {
                     if ($tool['visibility'] == '1' || $tool['name'] == TOOL_TRACKING) {
                         $link['name'] = Display::returnFontAwesomeIcon('minus');
@@ -758,11 +761,9 @@ class CourseHome
         }
 
         if (isset($tmp_all_tools_list)) {
+            $tbl_blogs_rel_user = Database::get_course_table(TABLE_BLOGS_REL_USER);
             foreach ($tmp_all_tools_list as $tool) {
                 if ($tool['image'] == 'blog.gif') {
-                    // Init
-                    $tbl_blogs_rel_user = Database::get_course_table(TABLE_BLOGS_REL_USER);
-
                     // Get blog id
                     $blog_id = substr($tool['link'], strrpos($tool['link'], '=') + 1, strlen($tool['link']));
 
@@ -793,14 +794,14 @@ class CourseHome
      * Displays the tools of a certain category.
      *
      * @param array $all_tools_list List of tools as returned by get_tools_category()
-     * @param bool  $rows
      *
      * @return array
      */
-    public static function show_tools_category($all_tools_list, $rows = false)
+    public static function show_tools_category($all_tools_list)
     {
         $_user = api_get_user_info();
         $theme = api_get_setting('homepage_view');
+
         if ($theme === 'vertical_activity') {
             //ordering by get_lang name
             $order_tool_list = [];
@@ -823,7 +824,6 @@ class CourseHome
         $session_id = api_get_session_id();
         $is_platform_admin = api_is_platform_admin();
         $allowEditionInSession = api_get_configuration_value('allow_edit_tool_visibility_in_session');
-
         if ($session_id == 0) {
             $is_allowed_to_edit = api_is_allowed_to_edit(null, true) && api_is_course_admin();
         } else {
@@ -833,7 +833,6 @@ class CourseHome
             }
         }
 
-        $i = 0;
         $items = [];
         $app_plugin = new AppPlugin();
 
@@ -844,7 +843,7 @@ class CourseHome
                 $studentview = false;
                 $tool['original_link'] = $tool['link'];
                 if ($tool['image'] == 'scormbuilder.gif') {
-                    // check if the published learnpath is visible for student
+                    // Check if the published learnpath is visible for student
                     $lpId = self::getPublishedLpIdFromLink($tool['link']);
                     if (api_is_allowed_to_edit(null, true)) {
                         $studentview = true;
@@ -983,26 +982,24 @@ class CourseHome
                     $tool['link'] = $web_code_path.$tool['link'];
                 }
 
+                $class = '';
                 if ($tool['visibility'] == '0' && $toolAdmin != '1') {
                     $class = 'text-muted';
                     $info = pathinfo($tool['image']);
                     $basename = basename($tool['image'], '.'.$info['extension']); // $file is set to "index"
                     $tool['image'] = $basename.'_na.'.$info['extension'];
-                } else {
-                    $class = '';
                 }
 
                 $qm_or_amp = strpos($tool['link'], '?') === false ? '?' : '&';
-                // If it's a link, we don't add the cidReq
 
+                // If it's a link, we don't add the cidReq
                 if ($tool['image'] == 'file_html.png' || $tool['image'] == 'file_html_na.png') {
                     $tool['link'] = $tool['link'];
                 } else {
                     $tool['link'] = $tool['link'].$qm_or_amp.api_get_cidreq();
                 }
 
-                $tool_link_params = [];
-                $toolIid = isset($tool["iid"]) ? $tool["iid"] : null;
+                $toolIid = isset($tool['iid']) ? $tool['iid'] : null;
 
                 //@todo this visio stuff should be removed
                 if (strpos($tool['name'], 'visio_') !== false) {
@@ -1050,6 +1047,7 @@ class CourseHome
                         .$tool['original_link'].$qm_or_amp.api_get_cidreq();
                 }
 
+                // Use in the course home
                 $icon = Display::return_icon(
                     $tool['image'],
                     $tool_name,
@@ -1058,6 +1056,24 @@ class CourseHome
                     false
                 );
 
+                // Used in the top bar
+                $iconMedium = Display::return_icon(
+                    $tool['image'],
+                    $tool_name,
+                    ['class' => 'tool-icon', 'id' => 'toolimage_'.$toolIid],
+                    ICON_SIZE_MEDIUM,
+                    false
+                );
+
+                // Used for vertical navigation
+                $iconSmall = Display::return_icon(
+                    $tool['image'],
+                    $tool_name,
+                    ['class' => 'tool-img', 'id' => 'toolimage_'.$toolIid],
+                    ICON_SIZE_SMALL,
+                    false
+                );
+
                 /*if (!empty($tool['custom_icon'])) {
                     $image = self::getCustomWebIconPath().$tool['custom_icon'];
                     $icon = Display::img(
@@ -1080,6 +1096,10 @@ class CourseHome
                 }
                 $item['url_params'] = $tool_link_params;
                 $item['icon'] = Display::url($icon, $tool_link_params['href'], $tool_link_params);
+                $item['only_icon'] = $icon;
+                $item['only_icon_medium'] = $iconMedium;
+                $item['only_icon_small'] = $iconSmall;
+                $item['only_href'] = $tool_link_params['href'];
                 $item['tool'] = $tool;
                 $item['name'] = $tool_name;
                 $tool_link_params['id'] = 'is'.$tool_link_params['id'];
@@ -1089,21 +1109,21 @@ class CourseHome
                     $tool_link_params
                 );
                 $items[] = $item;
-                $i++;
-            } // end of foreach
-        }
-
-        if (api_get_setting('homepage_view') != 'activity_big') {
-            return $items;
+            }
         }
 
         foreach ($items as &$item) {
-            $originalImage = self::getToolIcon($item);
-            $item['tool']['image'] = Display::url(
-                $originalImage,
-                $item['url_params']['href'],
-                $item['url_params']
-            );
+            $originalImage = self::getToolIcon($item, ICON_SIZE_BIG);
+            $item['tool']['only_icon_medium'] = self::getToolIcon($item, ICON_SIZE_MEDIUM, false);
+            $item['tool']['only_icon_small'] = self::getToolIcon($item, ICON_SIZE_SMALL, false);
+
+            if ($theme === 'activity_big') {
+                $item['tool']['image'] = Display::url(
+                    $originalImage,
+                    $item['url_params']['href'],
+                    $item['url_params']
+                );
+            }
         }
 
         return $items;
@@ -1182,13 +1202,15 @@ class CourseHome
 
         $toolName = Security::remove_XSS(stripslashes($tool['name']));
 
-        if (in_array($tool['image'], $already_translated_icons)) {
+        if (isset($tool['image']) && in_array($tool['image'], $already_translated_icons)) {
             return $toolName;
         }
 
         $toolName = api_underscore_to_camel_case($toolName);
 
-        if ('plugin' !== $tool['category'] && isset($GLOBALS['Tool'.$toolName])) {
+        if (isset($tool['category']) && 'plugin' !== $tool['category'] &&
+            isset($GLOBALS['Tool'.$toolName])
+        ) {
             return get_lang('Tool'.$toolName);
         }
 
@@ -1235,121 +1257,12 @@ class CourseHome
         return $category;
     }
 
-    /**
-     * @param bool $include_admin_tools
-     *
-     * @return array
-     */
-    public static function get_navigation_items($include_admin_tools = false)
-    {
-        $navigation_items = [];
-        $course_id = api_get_course_int_id();
-        $courseInfo = api_get_course_info();
-        $sessionId = api_get_session_id();
-
-        $conditionSession = api_get_session_condition(
-            $sessionId,
-            true,
-            true,
-            'session_id'
-        );
-
-        if (!empty($course_id)) {
-            $course_tools_table = Database::get_course_table(TABLE_TOOL_LIST);
-            /* 	Link to the Course homepage */
-            $navigation_items['home']['image'] = 'home.gif';
-            $navigation_items['home']['link'] = $courseInfo['course_public_url'];
-            $navigation_items['home']['name'] = get_lang('CourseHomepageLink');
-
-            $sql = "SELECT * FROM $course_tools_table
-                    WHERE 
-                      c_id = $course_id AND 
-                      visibility = '1' AND 
-                      admin = '0'
-                      $conditionSession
-                    ORDER BY id ASC";
-            $result = Database::query($sql);
-
-            $hideTools = [];
-            $hideToolsKeys = [];
-            if (!api_is_platform_admin()) {
-                $hideTools = api_get_setting('course_hide_tools');
-                $hideToolsKeys = array_keys($hideTools);
-            }
-
-            while ($row = Database::fetch_array($result)) {
-                if (!empty($hideTools)) {
-                    if (in_array($row['name'], $hideToolsKeys)) {
-                        // Tool is hidden
-                        if ($hideTools[$row['name']] == 'true') {
-                            continue;
-                        }
-                    }
-                }
-
-                $navigation_items[$row['id']] = $row;
-                if (stripos($row['link'], 'http://') === false &&
-                    stripos($row['link'], 'https://') === false
-                ) {
-                    $navigation_items[$row['id']]['link'] = api_get_path(WEB_CODE_PATH);
-
-                    if ($row['category'] == 'plugin') {
-                        $plugin = new AppPlugin();
-                        $pluginInfo = $plugin->getPluginInfo($row['name']);
-                        if (isset($pluginInfo['title'])) {
-                            $navigation_items[$row['id']]['link'] = api_get_path(WEB_PLUGIN_PATH);
-                            $navigation_items[$row['id']]['name'] = $pluginInfo['title'];
-                        }
-                    } else {
-                        $navigation_items[$row['id']]['name'] = self::translate_tool_name($row);
-                    }
-
-                    $navigation_items[$row['id']]['link'] .= $row['link'];
-                }
-            }
-
-            /* Admin (edit rights) only links
-            - Course settings (course admin only)
-            - Course rights (roles & rights overview) */
-            if ($include_admin_tools) {
-                $sql = "SELECT name, image FROM $course_tools_table
-                        WHERE c_id = $course_id  AND link='course_info/infocours.php'";
-                $sql_result = Database::query($sql);
-                $course_setting_info = Database::fetch_array($sql_result);
-                $course_setting_visual_name = self::translate_tool_name($course_setting_info);
-                if ($sessionId == 0) {
-                    // course settings item
-                    $navigation_items['course_settings']['image'] = $course_setting_info['image'];
-                    $navigation_items['course_settings']['link'] = api_get_path(WEB_CODE_PATH).'course_info/infocours.php';
-                    $navigation_items['course_settings']['name'] = $course_setting_visual_name;
-                }
-            }
-        }
-
-        foreach ($navigation_items as $key => $navigation_item) {
-            if (strstr($navigation_item['link'], '?')) {
-                //link already contains a parameter, add course id parameter with &
-                $parameter_separator = '&amp;';
-            } else {
-                //link doesn't contain a parameter yet, add course id parameter with ?
-                $parameter_separator = '?';
-            }
-            //$navigation_items[$key]['link'] .= $parameter_separator.api_get_cidreq();
-            $navigation_items[$key]['link'] .= $parameter_separator.'cidReq='.api_get_course_id().'&gidReq=0&id_session='.$sessionId;
-        }
-
-        return $navigation_items;
-    }
-
     /**
      * Show a navigation menu.
      */
     public static function show_navigation_menu()
     {
-        $navigation_items = self::get_navigation_items(true);
-
-        $course_id = api_get_course_id();
-
+        $blocks = self::getUserBlocks();
         $class = null;
         $idLearn = null;
         $item = null;
@@ -1357,62 +1270,37 @@ class CourseHome
 
         $html = '<div id="toolnav">';
         $html .= '<ul id="toolnavbox">';
-        $count = 0;
-        foreach ($navigation_items as $key => $navigation_item) {
-            //students can't see the course settings option
-            $count++;
-            if (!api_is_allowed_to_edit() && $key == 'course_settings') {
-                continue;
-            }
-            $html .= '<li>';
-            $url_item = parse_url($navigation_item['link']);
-            $url_current = parse_url($_SERVER['REQUEST_URI']);
-
-            if (api_get_setting('show_navigation_menu') == 'text') {
-                $class = 'text';
-                $marginLeft = 170;
-                $item = $navigation_item['name'];
-            } elseif (api_get_setting('show_navigation_menu') == 'icons') {
-                $class = 'icons';
-                $marginLeft = 25;
-                $item = Display::return_icon(
-                    substr($navigation_item['image'], 0, -3)."png",
-                    $navigation_item['name'],
-                    ['class' => 'tool-img'],
-                    ICON_SIZE_SMALL
-                );
-            } else {
-                $class = 'icons-text';
-                $item = $navigation_item['name'].
-                    Display::return_icon(
-                        substr($navigation_item['image'], 0, -3)."png",
-                        $navigation_item['name'],
-                        ['class' => 'tool-img'],
-                        ICON_SIZE_SMALL
-                    );
-            }
 
-            if (stristr($url_item['path'], $url_current['path'])) {
-                if (!isset($_GET['learnpath_id']) || strpos($url_item['query'], 'learnpath_id='.intval($_GET['learnpath_id'])) === 0) {
-                    $idLearn = ' id="here"';
+        $showOnlyText = api_get_setting('show_navigation_menu') === 'text';
+        $showOnlyIcons = api_get_setting('show_navigation_menu') === 'icons';
+
+        foreach ($blocks as $block) {
+            $blockItems = $block['content'];
+            foreach ($blockItems as $item) {
+                $html .= '<li>';
+                if ($showOnlyText) {
+                    $class = 'text';
+                    $marginLeft = 170;
+                    $show = $item['name'];
+                } elseif ($showOnlyIcons) {
+                    $class = 'icons';
+                    $marginLeft = 25;
+                    $show = $item['tool']['only_icon_small'];
+                } else {
+                    $class = 'icons-text';
+                    $show = $item['name'].$item['tool']['only_icon_small'];
                 }
-            }
 
-            if (strpos($navigation_item['link'], 'chat') !== false &&
-                api_get_course_setting('allow_open_chat_window', $course_id)
-            ) {
-                $html .= '<a '.$idLearn.' class="btn btn-default text-left '.$class.' " href="javascript: void(0);" onclick="javascript: window.open(\''.$navigation_item['link'].'\',\'window_chat'.api_get_course_id().'\',config=\'height=\'+600+\', width=\'+825+\', left=2, top=2, toolbar=no, menubar=no, scrollbars=yes, resizable=yes, location=no, directories=no, status=no\')" target="'.$navigation_item['target'].'"';
-                $html .= ' title="'.$navigation_item['name'].'">';
-                $html .= $item;
-                $html .= '</a>';
-            } else {
-                $html .= '<a '.$idLearn.' class="btn btn-default text-left '.$class.'" href="'.$navigation_item['link'].'" target="_top" title="'.$navigation_item['name'].'">';
-                $html .= $item;
-                $html .= '</a>';
+                $item['url_params']['class'] = 'btn btn-default text-left '.$class;
+                $html .= Display::url(
+                    $show,
+                    $item['only_href'],
+                    $item['url_params']
+                );
+                $html .= '</li>';
             }
-
-            $html .= '</li>';
         }
+
         $html .= '</ul>';
         $html .= '<script>$(function() {
                 $("#toolnavbox a").stop().animate({"margin-left":"-'.$marginLeft.'px"},1000);
@@ -1442,53 +1330,40 @@ class CourseHome
     public static function show_navigation_tool_shortcuts($orientation = SHORTCUTS_HORIZONTAL)
     {
         $origin = api_get_origin();
+        $courseInfo = api_get_course_info();
         if ($origin === 'learnpath') {
             return '';
         }
 
-        $navigation_items = self::get_navigation_items(false);
+        $blocks = self::getUserBlocks();
+
         $html = '';
-        if (!empty($navigation_items)) {
+        if (!empty($blocks)) {
+            $style_id = 'toolshortcuts_vertical';
             if ($orientation == SHORTCUTS_HORIZONTAL) {
-                $style_id = "toolshortcuts_horizontal";
-            } else {
-                $style_id = "toolshortcuts_vertical";
+                $style_id = 'toolshortcuts_horizontal';
             }
             $html .= '<div id="'.$style_id.'">';
-            foreach ($navigation_items as $key => $navigation_item) {
-                if (strpos($navigation_item['link'], 'chat') !== false &&
-                    api_get_course_setting('allow_open_chat_window')
-                ) {
-                    $html .= '<a class="items-icon" href="javascript: void(0);" onclick="javascript: window.open(\''.$navigation_item['link'].'\',\'window_chat'.api_get_course_id().'\',config=\'height=\'+600+\', width=\'+825+\', left=2, top=2, toolbar=no, menubar=no, scrollbars=yes, resizable=yes, location=no, directories=no, status=no\')" target="'.$navigation_item['target'].'"';
-                } else {
-                    $html .= '<a class="items-icon" href="'.$navigation_item['link'].'"';
-                }
-                if (strpos(api_get_self(), $navigation_item['link']) !== false) {
-                    $html .= ' id="here"';
-                }
-                $html .= ' target="_top" title="'.$navigation_item['name'].'">';
 
-                if (isset($navigation_item['category']) && $navigation_item['category'] == 'plugin') {
-                    /*$plugin_info = $app_plugin->getPluginInfo($navigation_item['name']);
-                    if (isset($plugin_info) && isset($plugin_info['title'])) {
-                        $tool_name = $plugin_info['title'];
-                    }*/
-                    if (!file_exists(api_get_path(SYS_CODE_PATH).'img/'.$navigation_item['image']) &&
-                        !file_exists(api_get_path(SYS_CODE_PATH).'img/icons/'.ICON_SIZE_MEDIUM.'/'.$navigation_item['image'])
-                    ) {
-                        $navigation_item['image'] = 'plugins.png';
-                    }
-                }
+            $html .= Display::url(
+                Display::return_icon('home.png', get_lang('CourseHomepageLink'), '', ICON_SIZE_MEDIUM),
+                $courseInfo['course_public_url'],
+                ['class' => 'items-icon']
+            );
 
-                $html .= Display::return_icon(
-                    substr($navigation_item['image'], 0, -3).'png',
-                    $navigation_item['name'],
-                    [],
-                    ICON_SIZE_MEDIUM
-                );
-                $html .= '</a> ';
-                if ($orientation == SHORTCUTS_VERTICAL) {
-                    $html .= '<br />';
+            foreach ($blocks as $block) {
+                $blockItems = $block['content'];
+                foreach ($blockItems as $item) {
+                    $item['url_params']['id'] = '';
+                    $item['url_params']['class'] = 'items-icon';
+                    $html .= Display::url(
+                        $item['tool']['only_icon_medium'],
+                        $item['only_href'],
+                        $item['url_params']
+                    );
+                    if ($orientation == SHORTCUTS_VERTICAL) {
+                        $html .= '<br />';
+                    }
                 }
             }
             $html .= '</div>';
@@ -1688,6 +1563,145 @@ class CourseHome
         }
     }
 
+    /**
+     * @return array
+     */
+    public static function getCourseAdminBlocks()
+    {
+        $blocks = [];
+        $my_list = self::get_tools_category(TOOL_AUTHORING);
+
+        $blocks[] = [
+            'title' => get_lang('Authoring'),
+            'class' => 'course-tools-author',
+            'content' => self::show_tools_category($my_list),
+        ];
+
+        $list1 = self::get_tools_category(TOOL_INTERACTION);
+        $list2 = self::get_tools_category(TOOL_COURSE_PLUGIN);
+        $my_list = array_merge($list1, $list2);
+
+        $blocks[] = [
+            'title' => get_lang('Interaction'),
+            'class' => 'course-tools-interaction',
+            'content' => self::show_tools_category($my_list),
+        ];
+
+        $my_list = self::get_tools_category(TOOL_ADMIN_PLATFORM);
+
+        $blocks[] = [
+            'title' => get_lang('Administration'),
+            'class' => 'course-tools-administration',
+            'content' => self::show_tools_category($my_list),
+        ];
+
+        return $blocks;
+    }
+
+    /**
+     * @return array
+     */
+    public static function getCoachBlocks()
+    {
+        $blocks = [];
+        $my_list = self::get_tools_category(TOOL_STUDENT_VIEW);
+
+        $blocks[] = [
+            'content' => self::show_tools_category($my_list),
+        ];
+
+        $sessionsCopy = api_get_setting('allow_session_course_copy_for_teachers');
+        if ($sessionsCopy === 'true') {
+            // Adding only maintenance for coaches.
+            $myList = self::get_tools_category(TOOL_ADMIN_PLATFORM);
+            $onlyMaintenanceList = [];
+
+            foreach ($myList as $item) {
+                if ($item['name'] === 'course_maintenance') {
+                    $item['link'] = 'course_info/maintenance_coach.php';
+
+                    $onlyMaintenanceList[] = $item;
+                }
+            }
+
+            $blocks[] = [
+                'title' => get_lang('Administration'),
+                'content' => self::show_tools_category($onlyMaintenanceList),
+            ];
+        }
+
+        return $blocks;
+    }
+
+    /**
+     * @return array
+     */
+    public static function getStudentBlocks()
+    {
+        $blocks = [];
+        $tools = self::get_tools_category(TOOL_STUDENT_VIEW);
+        $isDrhOfCourse = CourseManager::isUserSubscribedInCourseAsDrh(
+            api_get_user_id(),
+            api_get_course_info()
+        );
+
+        // Force user icon for DRH
+        if ($isDrhOfCourse) {
+            $addUserTool = true;
+            foreach ($tools as $tool) {
+                if ($tool['name'] === 'user') {
+                    $addUserTool = false;
+                    break;
+                }
+            }
+
+            if ($addUserTool) {
+                $tools[] = [
+                    'c_id' => api_get_course_int_id(),
+                    'name' => 'user',
+                    'link' => 'user/user.php',
+                    'image' => 'members.gif',
+                    'visibility' => '1',
+                    'admin' => '0',
+                    'address' => 'squaregrey.gif',
+                    'added_tool' => '0',
+                    'target' => '_self',
+                    'category' => 'interaction',
+                    'session_id' => api_get_session_id(),
+                ];
+            }
+        }
+
+        if (count($tools) > 0) {
+            $blocks[] = ['content' => self::show_tools_category($tools)];
+        }
+
+        if ($isDrhOfCourse) {
+            $drhTool = self::get_tools_category(TOOL_DRH);
+            $blocks[] = ['content' => self::show_tools_category($drhTool)];
+        }
+
+        return $blocks;
+    }
+
+    /**
+     * @return array
+     */
+    public static function getUserBlocks()
+    {
+        $sessionId = api_get_session_id();
+        // Start of tools for CourseAdmins (teachers/tutors)
+        if ($sessionId === 0 && api_is_course_admin() && api_is_allowed_to_edit(null, true)) {
+            $blocks = self::getCourseAdminBlocks();
+        } elseif (api_is_coach()) {
+            $blocks = self::getCoachBlocks();
+        } else {
+            $blocks = self::getStudentBlocks();
+        }
+
+        return $blocks;
+    }
+
     /**
      * Filter tool icons. Only show if $patronKey is = :teacher
      * Example dataIcons[i]['name']: parameter titleIcons1:teacher || titleIcons2 || titleIcons3:teacher.
@@ -1749,10 +1763,12 @@ class CourseHome
      * Find the tool icon when homepage_view is activity_big.
      *
      * @param array $item
+     * @param int   $iconSize
+     * @param bool  $generateId
      *
      * @return string
      */
-    private static function getToolIcon(array $item)
+    private static function getToolIcon(array $item, $iconSize, $generateId = true)
     {
         $image = str_replace('.gif', '.png', $item['tool']['image']);
         $toolIid = isset($item['tool']['iid']) ? $item['tool']['iid'] : null;
@@ -1779,11 +1795,16 @@ class CourseHome
             );
         }
 
+        $id = '';
+        if ($generateId) {
+            $id = 'toolimage_'.$toolIid;
+        }
+
         return Display::return_icon(
             $image,
             $item['name'],
-            ['id' => 'toolimage_'.$toolIid],
-            ICON_SIZE_BIG,
+            ['id' => $id],
+            $iconSize,
             false
         );
     }

+ 0 - 1
main/inc/lib/document.lib.php

@@ -4252,7 +4252,6 @@ class DocumentManager
             file_exists($documentData['absolute_path'])
         ) {
             $mp3FilePath = self::convertWavToMp3($documentData['absolute_path']);
-            error_log($mp3FilePath);
 
             if (!empty($mp3FilePath) && file_exists($mp3FilePath)) {
                 $documentId = self::addFileToDocumentTool(

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

@@ -598,7 +598,9 @@ class ExerciseLib
                             if (!empty($userChoiceList)) {
                                 foreach ($userChoiceList as $item) {
                                     $item = explode(':', $item);
-                                    $myChoice[$item[0]] = $item[1];
+                                    if (!empty($item)) {
+                                        $myChoice[$item[0]] = isset($item[1]) ? $item[1] : '';
+                                    }
                                 }
                             }
 
@@ -3204,7 +3206,7 @@ EOT;
             // only end is set
             $time_conditions .= " (start_time IS NULL AND end_time <> '' AND end_time > '$now') OR ";
             // nothing is set
-            $time_conditions .= " (start_time IS NULL AND end_time IS NULL))  ";
+            $time_conditions .= ' (start_time IS NULL AND end_time IS NULL)) ';
         }
 
         $needle_where = !empty($search) ? " AND title LIKE '?' " : '';

+ 3 - 1
main/inc/lib/exercise_show_functions.lib.php

@@ -187,7 +187,9 @@ class ExerciseShowFunctions
 
         if (empty($id)) {
             echo '<tr>';
-            echo Display::tag('td', Security::remove_XSS($answer), ['width' => '55%']);
+            if (!empty($answer)) {
+                echo Display::tag('td', Security::remove_XSS($answer), ['width' => '55%']);
+            }
             echo '</tr>';
             if (!$questionScore && $feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
                 echo '<tr>';

+ 1 - 0
main/inc/lib/fixlinks.js

@@ -109,6 +109,7 @@ $(document).ready(function() {
             src = url+'&type=link&src='+src;
             src = src.replace('https', 'http');
             $(this).attr('href', src);
+            $(this).attr('target', '_blank');
             var myAnchor = $('<a><img width="16px" src="'+iconPath+'link-external.png "/></a>').attr("href", src).attr('target', '_blank').attr('class', 'generated');
             $(this).after(myAnchor);
             $(this).after('-');

+ 100 - 10
main/inc/lib/formvalidator/Element/DateTimeRangePicker.php

@@ -21,7 +21,6 @@ class DateTimeRangePicker extends DateRangePicker
 
         $id = $this->getAttribute('id');
         $value = $this->getValue();
-        $label = $this->getLabel();
 
         if (!empty($value)) {
             $value = api_format_date($value, DATE_FORMAT_LONG_NO_DAY);
@@ -41,18 +40,109 @@ class DateTimeRangePicker extends DateRangePicker
                         <span class="sr-only">'.sprintf(get_lang('ResetFieldX'), $this->_label).'</span>
                     </button>
                 </span>                
-            </div>
-            <div class="input-group">
-                <br />
-                <p id="'.$id.'_time_range">
-                    <input type="text" name="'.$id.'_time_range_start" class="time start" autocomplete="off"> 
-                    '.get_lang('To').'
-                    <input type="text" name="'.$id.'_time_range_end" class="time end " autocomplete="off">
-                </p>
-            </div>
+            </div>            
         '.$this->getElementJS();
     }
 
+    /**
+     * @param string $layout
+     *
+     * @return string
+     */
+    public function getTemplate($layout)
+    {
+        $size = $this->getColumnsSize();
+
+        if (empty($size)) {
+            $sizeTemp = $this->getInputSize();
+            if (empty($size)) {
+                $sizeTemp = 8;
+            }
+            $size = [2, $sizeTemp, 2];
+        } else {
+            if (is_array($size)) {
+                if (count($size) != 3) {
+                    $sizeTemp = $this->getInputSize();
+                    if (empty($size)) {
+                        $sizeTemp = 8;
+                    }
+                    $size = [2, $sizeTemp, 2];
+                }
+                // else just keep the $size array as received
+            } else {
+                $size = [2, intval($size), 2];
+            }
+        }
+
+        $id = $this->getAttribute('id');
+
+        switch ($layout) {
+            case FormValidator::LAYOUT_INLINE:
+                return '
+                <div class="form-group {error_class}">
+                    <label {label-for} >
+                        <!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
+                        {label}
+                    </label>
+                    {element}
+                </div>';
+                break;
+            case FormValidator::LAYOUT_HORIZONTAL:
+                return '
+                <span id="'.$id.'_date_time_wrapper">
+                <div class="form-group {error_class}">
+                    <label {label-for} class="col-sm-'.$size[0].' control-label" >
+                        <!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
+                        {label}
+                    </label>
+                    <div class="col-sm-'.$size[1].'">
+                        {icon}
+                        {element}
+
+                        <!-- BEGIN label_2 -->
+                        <p class="help-block">{label_2}</p>
+                        <!-- END label_2 -->
+
+                        <!-- BEGIN error -->
+                        <span class="help-inline help-block">{error}</span>
+                        <!-- END error -->
+                    </div>
+                    <div class="col-sm-'.$size[2].'">
+                        <!-- BEGIN label_3 -->
+                            {label_3}
+                        <!-- END label_3 -->
+                    </div>            
+                </div>                
+                <div class="form-group {error_class}">
+                    <label class="col-sm-'.$size[0].' control-label" >
+                        <!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
+                        '.get_lang('Hour').'
+                    </label>
+                    <div class="col-sm-'.$size[1].'"> 
+                        <div class="input-group"> 
+                            <p id="'.$id.'_time_range">                    
+                                <input type="text" name="'.$id.'_time_range_start" class="time start" autocomplete="off"> 
+                                '.get_lang('To').'
+                                <input type="text" name="'.$id.'_time_range_end" class="time end " autocomplete="off">
+                            </p>
+                        </div>                   
+                    </div>
+                </div> 
+                </span>                   
+                ';
+                break;
+            case FormValidator::LAYOUT_BOX_NO_LABEL:
+                return '
+                        <label {label-for}>{label}</label>
+                        <div class="input-group">
+                            
+                            {icon}
+                            {element}
+                        </div>';
+                break;
+        }
+    }
+
     /**
      * Get the necessary javascript for this datepicker.
      *

+ 0 - 86
main/inc/lib/formvalidator/FormValidator.class.php

@@ -1286,92 +1286,6 @@ EOT;
         return $this->returnForm();
     }
 
-    /**
-     * Create a form validator based on an array of form data:.
-     *
-     *         array(
-     *             'name' => 'zombie_report_parameters',    //optional
-     *             'method' => 'GET',                       //optional
-     *             'items' => array(
-     *                 array(
-     *                     'name' => 'ceiling',
-     *                     'label' => 'Ceiling',            //optional
-     *                     'type' => 'date',
-     *                     'default' => date()              //optional
-     *                 ),
-     *                 array(
-     *                     'name' => 'active_only',
-     *                     'label' => 'ActiveOnly',
-     *                     'type' => 'checkbox',
-     *                     'default' => true
-     *                 ),
-     *                 array(
-     *                     'name' => 'submit_button',
-     *                     'type' => 'style_submit_button',
-     *                     'value' => get_lang('Search'),
-     *                     'attributes' => array('class' => 'search')
-     *                 )
-     *             )
-     *         );
-     *
-     * @param array $form_data
-     *
-     * @deprecated use normal FormValidator construct
-     *
-     * @return FormValidator
-     */
-    public static function create($form_data)
-    {
-        if (empty($form_data)) {
-            return null;
-        }
-        $form_name = isset($form_data['name']) ? $form_data['name'] : 'form';
-        $form_method = isset($form_data['method']) ? $form_data['method'] : 'POST';
-        $form_action = isset($form_data['action']) ? $form_data['action'] : '';
-        $form_target = isset($form_data['target']) ? $form_data['target'] : '';
-        $form_attributes = isset($form_data['attributes']) ? $form_data['attributes'] : null;
-        $form_track_submit = isset($form_data['track_submit']) ? $form_data['track_submit'] : true;
-        $reset = null;
-        $result = new FormValidator($form_name, $form_method, $form_action, $form_target, $form_attributes, $form_track_submit);
-
-        $defaults = [];
-        foreach ($form_data['items'] as $item) {
-            $name = $item['name'];
-            $type = isset($item['type']) ? $item['type'] : 'text';
-            $label = isset($item['label']) ? $item['label'] : '';
-            if ($type == 'wysiwyg') {
-                $element = $result->addHtmlEditor($name, $label);
-            } else {
-                $element = $result->addElement($type, $name, $label);
-            }
-            if (isset($item['attributes'])) {
-                $attributes = $item['attributes'];
-                $element->setAttributes($attributes);
-            }
-            if (isset($item['value'])) {
-                $value = $item['value'];
-                $element->setValue($value);
-            }
-            if (isset($item['default'])) {
-                $defaults[$name] = $item['default'];
-            }
-            if (isset($item['rules'])) {
-                $rules = $item['rules'];
-                foreach ($rules as $rule) {
-                    $message = $rule['message'];
-                    $type = $rule['type'];
-                    $format = isset($rule['format']) ? $rule['format'] : null;
-                    $validation = isset($rule['validation']) ? $rule['validation'] : 'server';
-                    $force = isset($rule['force']) ? $rule['force'] : false;
-                    $result->addRule($name, $message, $type, $format, $validation, $reset, $force);
-                }
-            }
-        }
-        $result->setDefaults($defaults);
-
-        return $result;
-    }
-
     /**
      * @return HTML_QuickForm_Renderer_Default
      */

+ 5 - 6
main/inc/lib/template.lib.php

@@ -468,17 +468,16 @@ class Template
 
         $this->assign('show_toolbar', $show_toolbar);
 
-        //Only if course is available
-        $show_course_shortcut = null;
-        $show_course_navigation_menu = null;
-
+        // Only if course is available
+        $show_course_shortcut = '';
+        $show_course_navigation_menu = '';
         if (!empty($this->course_id) && $this->user_is_logged_in) {
             if (api_get_setting('show_toolshortcuts') != 'false') {
-                //Course toolbar
+                // Course toolbar
                 $show_course_shortcut = CourseHome::show_navigation_tool_shortcuts();
             }
             if (api_get_setting('show_navigation_menu') != 'false') {
-                //Course toolbar
+                // Course toolbar
                 $show_course_navigation_menu = CourseHome::show_navigation_menu();
             }
         }

+ 16 - 7
main/inc/lib/zombie/zombie_manager.class.php

@@ -28,8 +28,14 @@ class ZombieManager
      *
      * @return ResultSet
      */
-    public static function listZombies($ceiling, $active_only = true, $count = 0, $from = 10, $column = 'user.firstname', $direction = 'desc')
-    {
+    public static function listZombies(
+        $ceiling,
+        $active_only = true,
+        $from = 0,
+        $count = 10,
+        $column = 'user.firstname',
+        $direction = 'desc'
+    ) {
         if (empty($column)) {
             $column = 'user.firstname';
         }
@@ -41,6 +47,7 @@ class ZombieManager
 
         $sql = 'SELECT
                     user.user_id,
+                    user.official_code,
                     user.firstname,
                     user.lastname,
                     user.username,
@@ -74,15 +81,17 @@ class ZombieManager
                         access.login_date <= '$ceiling' AND
                         user.user_id = access.login_user_id";
         }
+
         if ($active_only) {
             $sql .= ' AND user.active = 1';
         }
 
-        $count = intval($count);
-        $from = intval($from);
-
         $sql .= " ORDER BY $column $direction";
-        $sql .= " LIMIT $count, $from ";
+        if (!is_null($from) && !is_null($count)) {
+            $count = intval($count);
+            $from = intval($from);
+            $sql .= " LIMIT $from, $count ";
+        }
 
         $result = Database::query($sql);
 
@@ -94,7 +103,7 @@ class ZombieManager
      */
     public static function deactivate_zombies($ceiling)
     {
-        $zombies = self::list_zombies($ceiling);
+        $zombies = self::listZombies($ceiling);
         $ids = [];
         foreach ($zombies as $zombie) {
             $ids[] = $zombie['user_id'];

+ 45 - 75
main/inc/lib/zombie/zombie_report.class.php

@@ -34,9 +34,6 @@ class ZombieReport implements Countable
     public function get_parameters()
     {
         $result = [
-            'name' => 'zombie_report_parameters',
-            'method' => 'GET',
-            'attributes' => ['class' => 'well form-horizontal form-search'],
             'items' => [
                 [
                     'name' => 'ceiling',
@@ -44,10 +41,6 @@ class ZombieReport implements Countable
                     'type' => 'date_picker',
                     'default' => $this->get_ceiling('Y-m-d'),
                     'rules' => [
-//                        array(
-//                            'type' => 'required',
-//                            'message' => get_lang('Required')
-//                        ),
                         [
                             'type' => 'date',
                             'message' => get_lang('Date'),
@@ -68,14 +61,6 @@ class ZombieReport implements Countable
                 ],
             ],
         ];
-        $additional_parameters = $this->get_additional_parameters();
-        foreach ($additional_parameters as $key => $value) {
-            $result['items'][] = [
-                'type' => 'hidden',
-                'name' => $key,
-                'value' => $value,
-            ];
-        }
 
         return $result;
     }
@@ -85,31 +70,37 @@ class ZombieReport implements Countable
      */
     public function get_parameters_form()
     {
-        $parameters = $this->get_parameters();
-        if (empty($parameters)) {
-            return null;
-        }
-        if (empty($this->parameters_form)) {
-            $this->parameters_form = new FormValidator(
-                $parameters['name'],
-                $parameters['method'],
-                null,
-                null,
-                $parameters['attributes']
-            );
+        $form = new FormValidator(
+            'zombie_report_parameters',
+            'get',
+            null,
+            null,
+            ['class' => 'well form-horizontal form-search']
+        );
+
+        $form->addDatePicker('ceiling', get_lang('LastAccess'));
+        $form->addCheckBox('active_only', get_lang('ActiveOnly'));
+        $form->addButtonSearch(get_lang('Search'));
+
+        $params = [
+            'active_only' => $this->get_active_only(),
+            'ceiling' => $this->get_ceiling('Y-m-d'),
+        ];
+        $form->setDefaults($params);
+        $additional = $this->get_additional_parameters();
+        foreach ($additional as $key => $value) {
+            $value = Security::remove_XSS($value);
+            $form->addHidden($key, $value);
         }
 
-        return $this->parameters_form;
+        return $form;
     }
 
     public function display_parameters($return = false)
     {
         $form = $this->get_parameters_form();
-        if (empty($form)) {
-            return '';
-        }
-
         $result = $form->returnForm();
+
         if ($return) {
             return $result;
         } else {
@@ -120,9 +111,6 @@ class ZombieReport implements Countable
     public function is_valid()
     {
         $form = $this->get_parameters_form();
-        if (empty($form)) {
-            return true;
-        }
 
         return $form->isSubmitted() == false || $form->validate();
     }
@@ -175,57 +163,39 @@ class ZombieReport implements Countable
         }
 
         $action = $this->get_action();
-        $f = [$this, 'action_'.$action];
-        if (is_callable($f)) {
-            return call_user_func($f, $ids);
+        switch ($action) {
+            case 'activate':
+                return UserManager::activate_users($ids);
+                break;
+            case 'deactivate':
+                return UserManager::deactivate_users($ids);
+                break;
+            case 'delete':
+                return UserManager::delete_users($ids);
         }
 
         return false;
     }
 
-    public function action_deactivate($ids)
-    {
-        return UserManager::deactivate_users($ids);
-    }
-
-    public function action_activate($ids)
-    {
-        return UserManager::activate_users($ids);
-    }
-
-    public function action_delete($ids)
-    {
-        return UserManager::delete_users($ids);
-    }
-
     public function count()
     {
-        if (!$this->is_valid()) {
-            return 0;
-        }
-
         $ceiling = $this->get_ceiling();
         $active_only = $this->get_active_only();
-        $items = ZombieManager::listZombies($ceiling, $active_only);
+        $items = ZombieManager::listZombies($ceiling, $active_only, null, null);
 
         return count($items);
     }
 
     public function get_data($from, $count, $column, $direction)
     {
-        if (!$this->is_valid()) {
-            return [];
-        }
-
         $ceiling = $this->get_ceiling();
         $active_only = $this->get_active_only();
-
-        $items = ZombieManager::listZombies($ceiling, $active_only, $count, $from, $column, $direction);
+        $items = ZombieManager::listZombies($ceiling, $active_only, $from, $count, $column, $direction);
         $result = [];
         foreach ($items as $item) {
             $row = [];
             $row[] = $item['user_id'];
-            $row[] = $item['code'];
+            $row[] = $item['official_code'];
             $row[] = $item['firstname'];
             $row[] = $item['lastname'];
             $row[] = $item['username'];
@@ -253,12 +223,12 @@ class ZombieReport implements Countable
         $additional_parameters = $this->get_additional_parameters();
         $parameters = array_merge($additional_parameters, $parameters);
 
-        $table = new SortableTable('users', $count, $data, 1, 50);
+        $table = new SortableTable('zombie_users', $count, $data, 1, 50);
         $table->set_additional_parameters($parameters);
 
         $col = 0;
         $table->set_header($col++, '', false);
-        $table->set_header($col++, get_lang('Code'));
+        $table->set_header($col++, get_lang('OfficialCode'));
         $table->set_header($col++, get_lang('FirstName'));
         $table->set_header($col++, get_lang('LastName'));
         $table->set_header($col++, get_lang('LoginName'));
@@ -267,7 +237,7 @@ class ZombieReport implements Countable
         $table->set_header($col++, get_lang('AuthenticationSource'));
         $table->set_header($col++, get_lang('RegisteredDate'));
         $table->set_header($col++, get_lang('LastAccess'), false);
-        $table->set_header($col++, get_lang('Active'), false);
+        $table->set_header($col, get_lang('Active'), false);
 
         $table->set_column_filter(5, [$this, 'format_email']);
         $table->set_column_filter(6, [$this, 'format_status']);
@@ -295,7 +265,7 @@ class ZombieReport implements Countable
      */
     public function format_active($active)
     {
-        $active = ($active == '1');
+        $active = $active == '1';
         if ($active) {
             $image = 'accept';
             $text = get_lang('Yes');
@@ -324,14 +294,14 @@ class ZombieReport implements Countable
     public function display($return = false)
     {
         $result = $this->display_parameters($return);
-        if ($this->perform_action()) {
-            if ($return) {
-                $result .= Display::return_message(get_lang('Done'), 'confirmation');
-            } else {
-                echo Display::return_message(get_lang('Done'), 'confirmation');
-            }
+        $valid = $this->perform_action();
+
+        if ($valid) {
+            echo Display::return_message(get_lang('Updated'), 'confirmation');
         }
+
         $result .= $this->display_data($return);
+
         if ($return) {
             return $result;
         }

+ 19 - 1
main/install/configuration.dist.php

@@ -1018,12 +1018,30 @@ VALUES (2, 13, 'session_courses_read_only_mode', 'Lock Course In Session', 1, 1,
 // Activate the view with ViewerJS for PDF files within the lessons for IPad and IPhone
 // $_configuration['allow_pdf_viewerjs_in_lp'] = false;
 
-// Allow DRH user to access all session course announcements
+// Allow DRH user to access all visible session course announcements
 // $_configuration['allow_drh_access_announcement'] = false;
 
 // Allow my personal files link in the homepage
 // $_configuration['allow_my_files_link_in_homepage'] = false;
 
+// This option sets default parameters in the main/session/session_import.php
+/*$_configuration['session_import_settings'] = [
+    'options' =>  [
+        'session_exists_default_option' => '1',
+        'send_mail_default_option' => '1',
+    ]
+];*/
+
+// This option sets default parameters in the main/admin/user_import.php
+/*$_configuration['user_import_settings'] = [
+    'options' =>  [
+        'send_mail_default_option' => '1',
+    ]
+];*/
+
+// Disable all new exercise attempts in all the platform
+// $_configuration['exercises_disable_new_attempts'] = false;
+
 // ------ Custom DB changes (keep this at the end)
 // Add user activation by confirmation email
 // This option prevents the new user to login in the platform if your account is not confirmed via email

+ 0 - 2
main/lp/learnpath.class.php

@@ -3721,7 +3721,6 @@ class learnpath
                             }
 
                             $type_quiz = false;
-
                             foreach ($list as $toc) {
                                 if ($toc['id'] == $lp_item_id && ($toc['type'] == 'quiz')) {
                                     $type_quiz = true;
@@ -3755,7 +3754,6 @@ class learnpath
                     if (strpos($document_name, '_DELETED_')) {
                         $file = 'blank.php?error=document_deleted';
                     }
-
                     break;
                 case 2:
                     if ($this->debug > 2) {

+ 1 - 0
main/lp/scorm_api.php

@@ -2372,6 +2372,7 @@ function attach_glossary_into_scorm(type) {
                         src = url+'&type=link&src='+src;
                         src = src.replace('https', 'http');
                         $(this).attr('href', src);
+                        $(this).attr('target', '_blank');
                         var myAnchor = $('<a><img width="16px" src="<?php echo Display::returnIconPath('link-external.png'); ?>"/></a>').attr("href", src).attr('target', '_blank').attr('class', 'generated');
                         $(this).after(myAnchor);
                         $(this).after('-');

+ 6 - 2
main/mySpace/exercise_category_report.php

@@ -35,7 +35,7 @@ $(document).ready( function() {
         var value = option.id;        
         var selectedDate = $("#start_date").datepicker({ dateFormat: \'dd,MM,yyyy\' }).val();        
         window.location.replace("'.$currentUrl.'?start_date="+selectedDate+"&course_id="+value);
-    });	
+    });
 });
 </script>';
 
@@ -94,7 +94,11 @@ if ($form->validate() && !empty($courseInfo)) {
     $exerciseId = isset($values['exercise_id']) ? $values['exercise_id'] : 0;
     $startDate = Security::remove_XSS($values['start_date']);
     $exportFilename = 'exercise_results_report_'.$exerciseId.'_'.$courseInfo['code'];
-    $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_exercise_results_report&exercise_id='.$exerciseId.'&start_date='.$startDate.'&cidReq='.$courseInfo['code'].'&export_filename='.$exportFilename;
+    $url = api_get_path(WEB_AJAX_PATH).
+        'model.ajax.php?a=get_exercise_results_report&exercise_id='.$exerciseId.
+        '&start_date='.$startDate.'&cidReq='.$courseInfo['code'].
+        '&course_id='.$courseId.
+        '&export_filename='.$exportFilename;
 
     $categoryList = TestCategory::getListOfCategoriesIDForTest($exerciseId, $courseId);
     $columns = [

+ 1 - 1
main/mySpace/lp_tracking.php

@@ -90,7 +90,7 @@ $actions[] = Display::url(
 );
 $actions[] = Display::url(
     Display::return_icon('printer.png', get_lang('Print'), '', ICON_SIZE_MEDIUM),
-    'window.print();'
+    'javascript:window.print();'
 );
 $actions[] = Display::url(
     Display::return_icon('export_csv.png', get_lang('ExportAsCSV'), '', ICON_SIZE_MEDIUM),

+ 11 - 0
main/session/session_import.php

@@ -511,6 +511,17 @@ $form->addElement('checkbox', 'sendMail', null, get_lang('SendMailToUsers'));
 $form->addButtonImport(get_lang('ImportSession'));
 
 $defaults = ['sendMail' => 'true', 'file_type' => 'csv'];
+
+$options = api_get_configuration_value('session_import_settings');
+if (!empty($options) && isset($options['options'])) {
+    if (isset($options['options']['session_exists_default_option'])) {
+        $defaults['overwrite'] = $options['options']['session_exists_default_option'];
+    }
+    if (isset($options['options']['send_mail_default_option'])) {
+        $defaults['sendMail'] = $options['options']['send_mail_default_option'];
+    }
+}
+
 $form->setDefaults($defaults);
 
 Display::return_message(get_lang('TheXMLImportLetYouAddMoreInfoAndCreateResources'));

+ 10 - 9
main/social/invitations.php

@@ -12,7 +12,7 @@ require_once __DIR__.'/../inc/global.inc.php';
 api_block_anonymous_users();
 
 if (api_get_setting('allow_social_tool') !== 'true') {
-    api_not_allowed();
+    api_not_allowed(true);
 }
 
 $this_section = SECTION_SOCIAL;
@@ -95,23 +95,22 @@ $pending_invitations = $userGroupModel->get_groups_by_user(
     $user_id,
     GROUP_USER_PERMISSION_PENDING_INVITATION
 );
-$number_loop = count($list_get_invitation);
+$numberLoop = count($list_get_invitation);
 
-$total_invitations = $number_loop + count($list_get_invitation_sent) + count($pending_invitations);
+$total_invitations = $numberLoop + count($list_get_invitation_sent) + count($pending_invitations);
 
-if ($total_invitations == 0 && count($_GET) <= 0) {
+if (count($_GET) <= 0) {
     $socialInvitationsBlock .= '<div class="row">
         <div class="col-md-12">
             <a class="btn btn-success" href="search.php"><em class="fa fa-search"></em> '.
                 get_lang('TryAndFindSomeFriends').'
             </a>
             </div>
-        </div>';
+        </div><br />';
 }
 
-if ($number_loop != 0) {
+if ($numberLoop != 0) {
     $invitationHtml = '';
-
     foreach ($list_get_invitation as $invitation) {
         $sender_user_id = $invitation['user_sender_id'];
         $user_info = api_get_user_info($sender_user_id);
@@ -178,10 +177,12 @@ if (count($list_get_invitation_sent) > 0) {
 
         $invitationSentHtml .= '<div class="row">';
         $invitationSentHtml .= '<div class="col-md-3">';
-        $invitationSentHtml .= '<a href="profile.php?u='.$sender_user_id.'"><img src="'.$user_info['avatar'].'"  /></a>';
+        $invitationSentHtml .= '<a href="profile.php?u='.$sender_user_id.'">';
+        $invitationSentHtml .= '<img class="img-responsive img-rounded" src="'.$user_info['avatar'].'" /></a>';
         $invitationSentHtml .= '</div>';
         $invitationSentHtml .= '<div class="col-md-9">';
-        $invitationSentHtml .= '<h4 class="title-profile"><a class="profile_link" href="profile.php?u='.$sender_user_id.'">'.$user_info['complete_name'].'</a></h4>';
+        $invitationSentHtml .= '<h4 class="title-profile">
+            <a class="profile_link" href="profile.php?u='.$sender_user_id.'">'.$user_info['complete_name'].'</a></h4>';
         $invitationSentHtml .= '<div class="content-invitation">'.$title.' : '.$content.'</div>';
         $invitationSentHtml .= '<div class="date-invitation">'.get_lang('DateSend').' : '.$date.'</div>';
         $invitationSentHtml .= '</div>';

+ 0 - 1
main/survey/ch_yesno.php

@@ -65,7 +65,6 @@ class ch_yesno extends survey_question
             if (!empty($questionData['is_required'])) {
                 $radioAttributes['required'] = 'required';
             }
-
             $form->addRadio(
                 $name,
                 null,

+ 4 - 4
main/survey/create_meeting.php

@@ -68,9 +68,9 @@ $hideList = '';
 $maxEvents = 20;
 for ($i = 1; $i <= $maxEvents; $i++) {
     $name = 'time_'.$i;
-    $form->addDateTimeRangePicker($name, get_lang('Time'));
+    $form->addDateTimeRangePicker($name, get_lang('Date'));
     if ($i > 3) {
-        $hideList .= "$('#".$name."_alt').parent().parent().parent().hide();";
+        $hideList .= "$('#".$name."_date_time_wrapper').hide();";
     }
 }
 
@@ -80,12 +80,12 @@ $(function() {
     var number = 3;    
     $("#add_button").on("click", function() {
         number++;
-        $("#time_" + number + "_alt").parent().parent().parent().show();
+        $("#time_" + number + "_date_time_wrapper").show();
     });
     
     $("#remove_button").on("click", function() {
         if (number > 1) {            
-            $("#time_" + number + "_alt").parent().parent().parent().hide();
+            $("#time_" + number + "_date_time_wrapper").hide();
             number--;
         }
     });

+ 3 - 4
main/survey/create_new_survey.php

@@ -254,10 +254,9 @@ if ($_GET['action'] == 'add') {
     $defaults['parent_id'] = 0;
 }
 
-if (isset($survey_data['survey_type']) && $survey_data['survey_type'] == 1 || $_GET['action'] == 'add') {
-    $form->addElement('checkbox', 'one_question_per_page', null, get_lang('OneQuestionPerPage'));
-    $form->addElement('checkbox', 'shuffle', null, get_lang('ActivateShuffle'));
-}
+$form->addElement('checkbox', 'one_question_per_page', null, get_lang('OneQuestionPerPage'));
+$form->addElement('checkbox', 'shuffle', null, get_lang('ActivateShuffle'));
+
 $input_name_list = null;
 
 if (isset($_GET['action']) && $_GET['action'] == 'edit' && !empty($survey_id)) {

+ 36 - 44
main/survey/fillsurvey.php

@@ -657,9 +657,10 @@ if (
     // As long as there is no pagebreak fount we keep adding questions to the page
     $questions_displayed = [];
     $counter = 0;
-    $paged_questions = Session::read('paged_questions');
+    //$paged_questions = Session::read('paged_questions');
+    $paged_questions = [];
     // If non-conditional survey
-    if ($survey_data['survey_type'] === '0') {
+    if ($survey_data['survey_type'] == '0') {
         if (empty($paged_questions)) {
             $sql = "SELECT * FROM $table_survey_question
                     WHERE
@@ -670,16 +671,17 @@ if (
             $result = Database::query($sql);
             while ($row = Database::fetch_array($result, 'ASSOC')) {
                 if ($survey_data['one_question_per_page'] == 1) {
-                    $paged_questions[$counter][] = $row['question_id'];
-                    $counter++;
-
-                    continue;
-                }
-                if ($row['type'] == 'pagebreak') {
-                    $counter++;
+                    if ($row['type'] != 'pagebreak') {
+                        $paged_questions[$counter][] = $row['question_id'];
+                        $counter++;
+                        continue;
+                    }
                 } else {
-                    // ids from question of the current survey
-                    $paged_questions[$counter][] = $row['question_id'];
+                    if ($row['type'] == 'pagebreak') {
+                        $counter++;
+                    } else {
+                        $paged_questions[$counter][] = $row['question_id'];
+                    }
                 }
             }
             Session::write('paged_questions', $paged_questions);
@@ -741,7 +743,7 @@ if (
                             ".($allowRequiredSurveyQuestions ? ', survey_question.is_required' : '')."
                         FROM $table_survey_question survey_question
                         LEFT JOIN $table_survey_question_option survey_question_option
-                            ON survey_question.question_id = survey_question_option.question_id AND
+                        ON survey_question.question_id = survey_question_option.question_id AND
                             survey_question_option.c_id = $course_id
                         WHERE                        
                             survey_question NOT LIKE '%{{%' AND
@@ -768,10 +770,11 @@ if (
                     $questions[$row['sort']]['maximum_score'] = $row['max_value'];
                     $questions[$row['sort']]['sort'] = $row['sort'];
                     $questions[$row['sort']]['is_required'] = $allowRequiredSurveyQuestions && $row['is_required'];
-                } else {
+                }
+                /*} else {
                     // If the type is a pagebreak we are finished loading the questions for this page
                     break;
-                }
+                }*/
                 $counter++;
             }
         }
@@ -1171,7 +1174,7 @@ if (
                         $questions[$row['sort']]['sort'] = $row['sort'];
                     } else {
                         // If the type is a page break we are finished loading the questions for this page
-                        break;
+                        //break;
                     }
                     $counter++;
                 }
@@ -1182,23 +1185,15 @@ if (
     }
 }
 
-// Selecting the maximum number of pages
-$sql = "SELECT * FROM $table_survey_question
-        WHERE        
-            c_id = $course_id AND
-            type = 'pagebreak' AND
-            survey_id='".intval($survey_invitation['survey_id'])."'";
-$result = Database::query($sql);
-$numberofpages = Database::num_rows($result) + 1;
+$numberOfPages = SurveyManager::getCountPages($survey_data);
+
 // Displaying the form with the questions
+$show = 0;
 if (isset($_GET['show']) && $_GET['show'] != '') {
     $show = (int) $_GET['show'] + 1;
-} else {
-    $show = 0;
 }
 
 $displayFinishButton = true;
-
 if (isset($_GET['show']) && $_GET['show'] != '') {
     $pagesIndexes = array_keys($paged_questions);
     $pagesIndexes[] = count($pagesIndexes);
@@ -1209,10 +1204,9 @@ if (isset($_GET['show']) && $_GET['show'] != '') {
 }
 
 // Displaying the form with the questions
+$personality = 0;
 if (isset($_POST['personality'])) {
     $personality = (int) $_POST['personality'] + 1;
-} else {
-    $personality = 0;
 }
 
 // Displaying the form with the questions
@@ -1239,7 +1233,6 @@ $form->addHidden('language', $p_l);
 
 if (isset($questions) && is_array($questions)) {
     $originalShow = isset($_GET['show']) ? (int) $_GET['show'] : 0;
-
     $questionCounter = 1;
     if (!empty($originalShow)) {
         $before = 0;
@@ -1253,7 +1246,6 @@ if (isset($questions) && is_array($questions)) {
 
     foreach ($questions as $key => &$question) {
         $ch_type = 'ch_'.$question['type'];
-        //$questionNumber = $question['sort'];
         $questionNumber = $questionCounter;
         $display = new $ch_type();
         // @todo move this in a function.
@@ -1290,10 +1282,10 @@ if (isset($questions) && is_array($questions)) {
 }
 
 $form->addHtml('<div class="start-survey">');
-if ($survey_data['survey_type'] === '0') {
+if ($survey_data['survey_type'] == '0') {
     if ($survey_data['show_form_profile'] == 0) {
         // The normal survey as always
-        if (($show < $numberofpages)) {
+        if (($show < $numberOfPages)) {
             if ($show == 0) {
                 $form->addButton(
                     'next_survey_page',
@@ -1310,7 +1302,7 @@ if ($survey_data['survey_type'] === '0') {
                 );
             }
         }
-        if ($show >= $numberofpages && $displayFinishButton) {
+        if ($show >= $numberOfPages && $displayFinishButton) {
             $form->addButton(
                 'finish_survey',
                 get_lang('FinishSurvey'),
@@ -1321,8 +1313,8 @@ if ($survey_data['survey_type'] === '0') {
     } else {
         // The normal survey as always but with the form profile
         if (isset($_GET['show'])) {
-            $numberofpages = count($paged_questions);
-            if (($show < $numberofpages)) { //$show = $_GET['show'] + 1
+            $numberOfPages = count($paged_questions);
+            if (($show < $numberOfPages)) { //$show = $_GET['show'] + 1
                 if ($show == 0) {
                     $form->addButton(
                         'next_survey_page',
@@ -1340,7 +1332,7 @@ if ($survey_data['survey_type'] === '0') {
                 }
             }
 
-            if ($show >= $numberofpages && $displayFinishButton) {
+            if ($show >= $numberOfPages && $displayFinishButton) {
                 $form->addButton(
                     'finish_survey',
                     get_lang('FinishSurvey'),
@@ -1350,13 +1342,13 @@ if ($survey_data['survey_type'] === '0') {
             }
         }
     }
-} elseif ($survey_data['survey_type'] === '1') {
-    //conditional/personality-test type survey
+} elseif ($survey_data['survey_type'] == '1') {
+    // Conditional/personality-test type survey
     if (isset($_GET['show']) || isset($_POST['personality'])) {
-        $numberofpages = count($paged_questions);
+        $numberOfPages = count($paged_questions);
         if (!empty($paged_questions_sec) && count($paged_questions_sec) > 0) {
             // In case we're in the second phase, also sum the second group questions
-            $numberofpages += count($paged_questions_sec);
+            $numberOfPages += count($paged_questions_sec);
         } else {
             // We need this variable only if personality == 1
             Session::erase('page_questions_sec');
@@ -1364,7 +1356,7 @@ if ($survey_data['survey_type'] === '0') {
         }
 
         if ($personality == 0) {
-            if (($show <= $numberofpages) || !$_GET['show']) {
+            if (($show <= $numberOfPages) || !$_GET['show']) {
                 $form->addButton('next_survey_page', get_lang('Next'), 'arrow-right', 'success');
                 if ($survey_data['one_question_per_page'] == 0) {
                     if ($personality >= 0) {
@@ -1376,17 +1368,17 @@ if ($survey_data['survey_type'] === '0') {
                     }
                 }
 
-                if ($numberofpages == $show) {
+                if ($numberOfPages == $show) {
                     $form->addHidden('personality', $personality);
                 }
             }
         }
 
-        if ($show > $numberofpages && $_GET['show'] && $personality == 0) {
+        if ($show > $numberOfPages && $_GET['show'] && $personality == 0) {
             $form->addHidden('personality', $personality);
         } elseif ($personality > 0) {
             if ($survey_data['one_question_per_page'] == 1) {
-                if ($show >= $numberofpages) {
+                if ($show >= $numberOfPages) {
                     $form->addButton('finish_survey', get_lang('FinishSurvey'), 'arrow-right', 'success');
                 } else {
                     $form->addHidden('personality', $personality);

+ 56 - 14
main/survey/meeting.php

@@ -94,16 +94,37 @@ if (isset($_POST) && !empty($_POST)) {
     }
 
     $status = 1;
-    foreach ($options as $selectedQuestionId) {
-        SurveyUtil::store_answer(
-            $userId,
-            $surveyId,
-            $selectedQuestionId,
-            1,
-            $status,
-            $surveyData
-        );
+    if (!empty($options)) {
+        foreach ($options as $selectedQuestionId) {
+            SurveyUtil::store_answer(
+                $userId,
+                $surveyId,
+                $selectedQuestionId,
+                1,
+                $status,
+                $surveyData
+            );
+        }
+    } else {
+        foreach ($questions as $item) {
+            $questionId = $item['question_id'];
+            SurveyUtil::store_answer(
+                $userId,
+                $surveyId,
+                $questionId,
+                1,
+                0,
+                $surveyData
+            );
+        }
     }
+
+    SurveyManager::update_survey_answered(
+        $surveyData,
+        $survey_invitation['user'],
+        $survey_invitation['survey_code']
+    );
+
     Display::addFlash(Display::return_message(get_lang('Saved')));
     header('Location: '.$url);
     exit;
@@ -141,17 +162,26 @@ foreach ($questions as $item) {
 
 $row = 1;
 $column = 0;
+
 // Total counter
 $table->setHeaderContents(
     $row,
     0,
     get_lang('NumberOfUsers').': '.count($students)
 );
+
 foreach ($questions as $item) {
+    $questionId = $item['question_id'];
     $count = 0;
-    if (isset($answerList[$item['question_id']])) {
+    $questionsWithAnswer = 0;
+    if (isset($answerList[$questionId])) {
+        foreach ($answerList[$questionId] as $userAnswer) {
+            if ((int) $userAnswer === 1) {
+                $questionsWithAnswer++;
+            }
+        }
         $count = '<p style="color:cornflowerblue" >
-            <span class="fa fa-check fa-2x"></span>'.count($answerList[$item['question_id']]).'</p>';
+                  <span class="fa fa-check fa-2x"></span>'.$questionsWithAnswer.'</p>';
     }
     $table->setHeaderContents(
         $row,
@@ -162,6 +192,9 @@ foreach ($questions as $item) {
 
 $row = 2;
 $column = 0;
+$availableIcon = Display::return_icon('bullet_green.png', get_lang('Available'));
+$notAvailableIcon = Display::return_icon('bullet_red.png', get_lang('NotAvailable'));
+
 foreach ($students as $studentId) {
     $userInfo = api_get_user_info($studentId);
     $name = $userInfo['complete_name'];
@@ -177,9 +210,15 @@ foreach ($students as $studentId) {
             $checked = '';
             $html = '';
             if (isset($answerList[$item['question_id']][$studentId])) {
-                $checked = Display::return_icon('check-circle.png');
+                $checked = $availableIcon;
+                if (empty($answerList[$item['question_id']][$studentId])) {
+                    $checked = $notAvailableIcon;
+                }
                 if ($action === 'edit') {
-                    $checked = 'checked';
+                    $checked = '';
+                    if ($answerList[$item['question_id']][$studentId] == 1) {
+                        $checked = 'checked';
+                    }
                 }
             }
 
@@ -206,7 +245,10 @@ foreach ($students as $studentId) {
         foreach ($questions as $item) {
             $checked = '';
             if (isset($answerList[$item['question_id']][$studentId])) {
-                $checked = Display::return_icon('check-circle.png');
+                $checked = $availableIcon;
+                if (empty($answerList[$item['question_id']][$studentId])) {
+                    $checked = $notAvailableIcon;
+                }
             }
             $table->setHeaderContents(
                 $row,

+ 21 - 32
main/survey/preview.php

@@ -83,6 +83,7 @@ if ($surveyAnonymous == 0 && api_is_anonymous()) {
         api_not_allowed(true);
     }
 }
+$show = 0;
 
 Display::display_header(get_lang('SurveyPreview'));
 
@@ -105,7 +106,6 @@ if (api_is_course_admin() ||
         if (!empty($survey_data['survey_introduction'])) {
             echo '<div class="survey_content">'.$survey_data['survey_introduction'].'</div>';
         }
-        $limit = 0;
     }
 
     // Displaying the survey thanks message
@@ -127,33 +127,31 @@ if (api_is_course_admin() ||
         $sql = "SELECT * FROM $table_survey_question
                 WHERE
                   survey_question NOT LIKE '%{{%' AND 
-                  c_id = $course_id AND 
+                  c_id = $course_id AND
                   survey_id = '".$surveyId."'
                 ORDER BY sort ASC";
         $result = Database::query($sql);
         $questions_exists = true;
         if (Database::num_rows($result)) {
             while ($row = Database::fetch_array($result)) {
-                if ($row['type'] == 'pagebreak') {
-                    $counter++;
+                if ($survey_data['one_question_per_page'] == 1) {
+                    if ($row['type'] != 'pagebreak') {
+                        $paged_questions[$counter][] = $row['question_id'];
+                        $counter++;
+                        continue;
+                    }
                 } else {
-                    $paged_questions[$counter][] = $row['question_id'];
+                    if ($row['type'] == 'pagebreak') {
+                        $counter++;
+                    } else {
+                        $paged_questions[$counter][] = $row['question_id'];
+                    }
                 }
             }
         } else {
             $questions_exists = false;
         }
 
-        $sql = "SELECT count(survey_question.question_id) as count                        
-                FROM $table_survey_question survey_question
-                WHERE 
-                  survey_question.survey_id = '".$surveyId."' AND
-                  survey_question.c_id = $course_id AND 
-                  survey_question LIKE '%{{%' ";
-        $result = Database::query($sql);
-        $sourceQuestions = Database::fetch_array($result, 'ASSOC');
-        $sourceQuestions = $sourceQuestions['count'];
-
         if (array_key_exists($_GET['show'], $paged_questions)) {
             $sql = "SELECT
                         survey_question.question_id,
@@ -179,9 +177,6 @@ if (api_is_course_admin() ||
                     ORDER BY survey_question.sort, survey_question_option.sort ASC";
 
             $result = Database::query($sql);
-            $question_counter_max = Database::num_rows($result);
-            $limit = 0;
-
             while ($row = Database::fetch_array($result)) {
                 // If the type is not a pagebreak we store it in the $questions array
                 if ($row['type'] != 'pagebreak') {
@@ -191,11 +186,12 @@ if (api_is_course_admin() ||
                     $questions[$sort]['survey_question'] = $row['survey_question'];
                     $questions[$sort]['display'] = $row['display'];
                     $questions[$sort]['type'] = $row['type'];
-                    $questions[$sort]['options'][intval($row['option_sort'])] = $row['option_text'];
+                    //$questions[$sort]['options'][intval($row['option_sort'])] = $row['option_text'];
+                    $questions[$sort]['options'][$row['question_option_id']] = $row['option_text'];
                     $questions[$sort]['maximum_score'] = $row['max_value'];
                 } else {
                     // If the type is a pagebreak we are finished loading the questions for this page
-                    break;
+                    //break;
                 }
                 $counter_question++;
             }
@@ -207,18 +203,9 @@ if (api_is_course_admin() ||
         $before = count($paged_questions[$_GET['show'] - 1]);
     }
 
-    // Selecting the maximum number of pages
-    $sql = "SELECT * FROM $table_survey_question
-            WHERE
-                survey_question NOT LIKE '%{{%' AND 
-                c_id = $course_id AND
-                type = 'pagebreak' AND
-                survey_id = '".$surveyId."'";
-    $result = Database::query($sql);
-    $numberofpages = Database::num_rows($result) + 1;
+    $numberOfPages = SurveyManager::getCountPages($survey_data);
 
     // Displaying the form with the questions
-    $show = 0;
     if (isset($_GET['show'])) {
         $show = (int) $_GET['show'] + 1;
     }
@@ -257,7 +244,8 @@ if (api_is_course_admin() ||
         }
     }
     $form->addHtml('<div class="start-survey">');
-    if (($show < $numberofpages) || (!$_GET['show'] && count($questions) > 0)) {
+
+    if (($show < $numberOfPages)) {
         if ($show == 0) {
             $form->addButton(
                 'next_survey_page',
@@ -274,7 +262,8 @@ if (api_is_course_admin() ||
             );
         }
     }
-    if ($show >= $numberofpages && $_GET['show'] ||
+
+    if ($show >= $numberOfPages && isset($_GET['show']) ||
         (isset($_GET['show']) && count($questions) == 0)
     ) {
         if ($questions_exists == false) {

+ 57 - 9
main/survey/survey.lib.php

@@ -280,10 +280,11 @@ class SurveyManager
                 $extraParams['form_fields'] = '';
             }
 
+            $extraParams['one_question_per_page'] = isset($values['one_question_per_page']) ? $values['one_question_per_page'] : 0;
+            $extraParams['shuffle'] = isset($values['shuffle']) ? $values['shuffle'] : 0;
+
             if ($values['survey_type'] == 1) {
                 $extraParams['survey_type'] = 1;
-                $extraParams['shuffle'] = $values['shuffle'];
-                $extraParams['one_question_per_page'] = $values['one_question_per_page'];
                 $extraParams['parent_id'] = $values['parent_id'];
 
                 // Logic for versioning surveys
@@ -415,14 +416,9 @@ class SurveyManager
                 $values['anonymous'] = 0;
             }
 
-            $values['shuffle'] = isset($values['shuffle']) ? $values['shuffle'] : null;
-            $values['one_question_per_page'] = isset($values['one_question_per_page']) ? $values['one_question_per_page'] : null;
-            $values['show_form_profile'] = isset($values['show_form_profile']) ? $values['show_form_profile'] : null;
-
             $extraParams = [];
-            $extraParams['shuffle'] = $values['shuffle'];
-            $extraParams['one_question_per_page'] = $values['one_question_per_page'];
-            $extraParams['shuffle'] = $values['shuffle'];
+            $extraParams['one_question_per_page'] = isset($values['one_question_per_page']) ? $values['one_question_per_page'] : null;
+            $extraParams['shuffle'] = isset($values['shuffle']) ? $values['shuffle'] : null;
 
             if ($values['anonymous'] == 0) {
                 $extraParams['show_form_profile'] = $values['show_form_profile'];
@@ -2211,4 +2207,56 @@ class SurveyManager
             }
         }
     }
+
+    /**
+     * @param array $survey
+     *
+     * @return int
+     */
+    public static function getCountPages($survey)
+    {
+        if (empty($survey) || !isset($survey['iid'])) {
+            return 0;
+        }
+
+        $courseId = $survey['c_id'];
+        $surveyId = $survey['survey_id'];
+
+        $table = Database::get_course_table(TABLE_SURVEY_QUESTION);
+
+        // One question per page
+        $sql = "SELECT * FROM $table
+                WHERE
+                    survey_question NOT LIKE '%{{%' AND
+                    type = 'pagebreak' AND
+                    c_id = $courseId AND
+                    survey_id = '".$surveyId."'";
+        $result = Database::query($sql);
+        $numberPageBreaks = Database::num_rows($result);
+
+        // One question per page
+        $sql = "SELECT * FROM $table
+                    WHERE
+                        survey_question NOT LIKE '%{{%' AND
+                        type != 'pagebreak' AND
+                        c_id = $courseId AND
+                        survey_id = '".$surveyId."'";
+        $result = Database::query($sql);
+        $countOfQuestions = Database::num_rows($result);
+
+        $count = 1;
+        if (!empty($numberPageBreaks) && !empty($countOfQuestions)) {
+            // One question per page
+            $count = $countOfQuestions;
+        }
+
+        if ($survey['one_question_per_page'] == 1) {
+            $count = 1;
+            if (!empty($countOfQuestions)) {
+                $count = $countOfQuestions;
+            }
+        }
+
+        return $count;
+    }
 }

+ 7 - 4
main/survey/survey.php

@@ -196,10 +196,13 @@ if ($survey_data['survey_type'] == 0) {
         Display::return_icon('commentquestion.png', get_lang('Comment'), null, ICON_SIZE_BIG),
         $urlQuestion.'&type=comment&survey_id='.$survey_id
     );
-    echo Display::url(
-        Display::return_icon('page_end.png', get_lang('Pagebreak'), null, ICON_SIZE_BIG),
-        $urlQuestion.'&type=pagebreak&survey_id='.$survey_id
-    );
+
+    if ($survey_data['one_question_per_page'] == 0) {
+        echo Display::url(
+            Display::return_icon('page_end.png', get_lang('Pagebreak'), null, ICON_SIZE_BIG),
+            $urlQuestion.'&type=pagebreak&survey_id='.$survey_id
+        );
+    }
     echo '</div>';
 } else {
     if ($survey_data['survey_type'] != 3) {

+ 25 - 14
main/survey/survey_question.php

@@ -23,12 +23,13 @@ class survey_question
     public function createForm($surveyData, $formData)
     {
         $action = isset($_GET['action']) ? Security::remove_XSS($_GET['action']) : null;
-        $questionId = isset($_GET['question_id']) ? intval($_GET['question_id']) : null;
-        $surveyId = isset($_GET['survey_id']) ? intval($_GET['survey_id']) : null;
+        $questionId = isset($_GET['question_id']) ? (int) $_GET['question_id'] : null;
+        $surveyId = isset($_GET['survey_id']) ? (int) $_GET['survey_id'] : null;
+        $type = isset($_GET['type']) ? Security::remove_XSS($_GET['type']) : null;
 
         $toolName = Display::return_icon(
-            SurveyManager::icon_question(Security::remove_XSS($_GET['type'])),
-            get_lang(ucfirst(Security::remove_XSS($_GET['type']))),
+            SurveyManager::icon_question($type),
+            get_lang(ucfirst($type)),
             ['align' => 'middle', 'height' => '22px']
         ).' ';
 
@@ -49,19 +50,18 @@ class survey_question
                 $toolName .= get_lang('MultipleResponse');
                 break;
             default:
-                $toolName .= get_lang(api_ucfirst(Security::remove_XSS($_GET['type'])));
+                $toolName .= get_lang(api_ucfirst($type));
         }
 
         $sharedQuestionId = isset($formData['shared_question_id']) ? $formData['shared_question_id'] : null;
 
-        $url = api_get_self().'?action='.$action.'&type='.Security::remove_XSS($_GET['type']).'&survey_id='.$surveyId.'&question_id='.$questionId.'&'.api_get_cidreq();
-
+        $url = api_get_self().'?action='.$action.'&type='.$type.'&survey_id='.$surveyId.'&question_id='.$questionId.'&'.api_get_cidreq();
         $form = new FormValidator('question_form', 'post', $url);
         $form->addHeader($toolName);
         $form->addHidden('survey_id', $surveyId);
         $form->addHidden('question_id', $questionId);
         $form->addHidden('shared_question_id', Security::remove_XSS($sharedQuestionId));
-        $form->addHidden('type', Security::remove_XSS($_GET['type']));
+        $form->addHidden('type', $type);
 
         $config = [
             'ToolbarSet' => 'SurveyQuestion',
@@ -95,15 +95,27 @@ class survey_question
 
             $grouplist = $grouplist1 = $grouplist2 = $glist;
             if (!empty($formData['assigned'])) {
-                $grouplist = str_replace('<option value="'.$formData['assigned'].'"', '<option value="'.$formData['assigned'].'" selected', $glist);
+                $grouplist = str_replace(
+                    '<option value="'.$formData['assigned'].'"',
+                    '<option value="'.$formData['assigned'].'" selected',
+                    $glist
+                );
             }
 
             if (!empty($formData['assigned1'])) {
-                $grouplist1 = str_replace('<option value="'.$formData['assigned1'].'"', '<option value="'.$formData['assigned1'].'" selected', $glist);
+                $grouplist1 = str_replace(
+                    '<option value="'.$formData['assigned1'].'"',
+                    '<option value="'.$formData['assigned1'].'" selected',
+                    $glist
+                );
             }
 
             if (!empty($formData['assigned2'])) {
-                $grouplist2 = str_replace('<option value="'.$formData['assigned2'].'"', '<option value="'.$formData['assigned2'].'" selected', $glist);
+                $grouplist2 = str_replace(
+                    '<option value="'.$formData['assigned2'].'"',
+                    '<option value="'.$formData['assigned2'].'" selected',
+                    $glist
+                );
             }
 
             $this->html .= '	<tr><td colspan="">
@@ -131,14 +143,13 @@ class survey_question
      */
     public function renderForm()
     {
-        if (isset($_GET['question_id']) and !empty($_GET['question_id'])) {
+        if (isset($_GET['question_id']) && !empty($_GET['question_id'])) {
             /**
              * Check if survey has answers first before update it, this is because if you update it, the question
              * options will delete and re-insert in database loosing the iid and question_id to verify the correct answers.
              */
-            $surveyId = isset($_GET['survey_id']) ? intval($_GET['survey_id']) : 0;
+            $surveyId = isset($_GET['survey_id']) ? (int) $_GET['survey_id'] : 0;
             $answersChecker = SurveyUtil::checkIfSurveyHasAnswers($surveyId);
-
             if (!$answersChecker) {
                 $this->buttonList[] = $this->getForm()->addButtonUpdate(get_lang('ModifyQuestionSurvey'), 'save', true);
             } else {

+ 1 - 1
main/template/default/course_home/activity.tpl

@@ -12,7 +12,7 @@
             <div class="offset2 col-md-4 course-tool">
                 {{ item.extra }}
                 {{ item.visibility }}
-                {{ item.icon }}
+                {{ item.only_icon_small }}
                 {{ item.link }}
             </div>
             {% endfor %}

+ 7 - 7
main/template/default/layout/main.js.tpl

@@ -2,8 +2,8 @@
 var ajax_url = _p.web_ajax + 'chat.ajax.php';
 var online_button = '<img src="' + _p.web_img + 'statusonline.png">';
 var offline_button = '<img src="' + _p.web_img + 'statusoffline.png">';
-var connect_lang = '{{ "ChatConnected"|get_lang }}';
-var disconnect_lang = '{{ "ChatDisconnected"|get_lang }}';
+var connect_lang = '{{ "ChatConnected"|get_lang | escape('js')}}';
+var disconnect_lang = '{{ "ChatDisconnected"|get_lang | escape('js')}}';
 
 $(function() {
     addMainEvent(window, 'unload', courseLogout ,false);
@@ -101,7 +101,7 @@ $(function() {
     // Delete modal
     $('#confirm-delete').on('show.bs.modal', function(e) {
         $(this).find('.btn-ok').attr('href', $(e.relatedTarget).data('href'));
-        var message = '{{ 'AreYouSureToDeleteJS' | get_lang }}: <strong>' + $(e.relatedTarget).data('item-title') + '</strong>';
+        var message = '{{ 'AreYouSureToDeleteJS' | get_lang | escape('js')}}: <strong>' + $(e.relatedTarget).data('item-title') + '</strong>';
 
         if ($(e.relatedTarget).data('item-question')) {
             message = $(e.relatedTarget).data('item-question');
@@ -298,16 +298,16 @@ $(function() {
 
     $('.star-rating li a').on('click', function(event) {
         var id = $(this).parents('ul').attr('id');
-        $('#vote_label2_' + id).html("{{'Loading'|get_lang}}");
+        $('#vote_label2_' + id).html("{{'Loading'|get_lang| escape('js')}}");
         $.ajax({
             url: $(this).attr('data-link'),
             success: function(data) {
                 $("#rating_wrapper_"+id).html(data);
                 if (data == 'added') {
-                    //$('#vote_label2_' + id).html("{{'Saved'|get_lang}}");
+                    //$('#vote_label2_' + id).html("{{'Saved'|get_lang | escape('js')}}");
                 }
                 if (data == 'updated') {
-                    //$('#vote_label2_' + id).html("{{'Saved'|get_lang}}");
+                    //$('#vote_label2_' + id).html("{{'Saved'|get_lang| escape('js')}}");
                 }
             }
         });
@@ -420,7 +420,7 @@ function setCheckbox(value, table_id) {
 
 function action_click(element, table_id) {
     d = $("#"+table_id);
-    if (!confirm('{{ "ConfirmYourChoice"|get_lang }}')) {
+    if (!confirm('{{ "ConfirmYourChoice"|get_lang | escape('js')}}')) {
         return false;
     } else {
         var action =$(element).attr("data-action");

+ 12 - 13
main/template/default/layout/show_header.tpl

@@ -42,23 +42,22 @@
             {% include 'layout/page_header.tpl'|get_template %}
         </header>
         <!-- END HEADER -->
-
-
     {% endif %}
 
     <!-- START CONTENT -->
     <section id="cm-content">
         <div class="container">
-            {% block breadcrumb %}
-                {{ breadcrumb }}
-            {% endblock %}
+            {% if show_header == true %}
+                {% block breadcrumb %}
+                    {{ breadcrumb }}
+                {% endblock %}
 
-            {% if show_course_shortcut is not null %}
-                <!-- TOOLS SHOW COURSE -->
-                <div id="cm-tools" class="nav-tools">
-                    {{ show_course_shortcut }}
-                </div>
-                <!-- END TOOLS SHOW COURSE -->
+                {% if show_course_shortcut is not null %}
+                    <!-- TOOLS SHOW COURSE -->
+                    <div id="cm-tools" class="nav-tools">
+                        {{ show_course_shortcut }}
+                    </div>
+                    <!-- END TOOLS SHOW COURSE -->
+                {% endif %}
             {% endif %}
-
-            {{ flash_messages }}
+            {{ flash_messages }}

+ 7 - 3
main/tracking/courseLog.php

@@ -359,7 +359,11 @@ if ($nbStudents > 0) {
     );
 
     foreach ($usersTracking as $userTracking) {
-        $userId = UserManager::get_user_id_from_username($userTracking[3]);
+        $userInfo = api_get_user_info_from_username($userTracking[3]);
+        if (empty($userInfo)) {
+            continue;
+        }
+        $userId = $userInfo['user_id'];
         if ($userTracking[5] === '100%') {
             $numberStudentsCompletedLP++;
         }
@@ -386,10 +390,10 @@ if ($nbStudents > 0) {
 
         $listStudent = [
             'id' => $userId,
-            'fullname' => $userTracking[2].', '.$userTracking[1],
+            'fullname' => $userInfo['complete_name'],
             'score' => floor($scoreStudent / 2),
             'total_time' => $minutes,
-            'avatar' => UserManager::getUserPicture($userId),
+            'avatar' => $userInfo['avatar'],
             'certicate' => $certificate,
         ];
         $listStudentIds[] = $userId;

+ 0 - 2
plugin/courseblock/CourseBlockPlugin.php

@@ -50,8 +50,6 @@ class CourseBlockPlugin extends Plugin
         return $result ? $result : $result = new self();
     }
 
-    ///public function
-
     public function install()
     {
         // Installing course settings

+ 0 - 3
plugin/dictionary/terms.php

@@ -75,9 +75,6 @@ switch ($action) {
 $tpl = new Template($plugin->get_lang('plugin_title'));
 $tpl->assign('terms', $terms);
 $tpl->assign('form', $form->returnForm());
-
-//$url = api_get_path(WEB_PLUGIN_PATH).'studentfollowup/posts.php?';
-//$tpl->assign('post_url', $url);
 $content = $tpl->fetch('/'.$plugin->get_name().'/view/terms.html.twig');
 // Assign into content
 $tpl->assign('content', $content);

+ 1 - 1
plugin/ims_lti/ImsLtiPlugin.php

@@ -29,7 +29,7 @@ class ImsLtiPlugin extends Plugin
      */
     protected function __construct()
     {
-        $version = '1.1 (beta)';
+        $version = '1.5 (beta)';
         $author = 'Angel Fernando Quiroz Campos';
 
         parent::__construct($version, $author, ['enabled' => 'boolean']);

+ 0 - 621
plugin/ims_lti/OAuthSimple.php

@@ -1,621 +0,0 @@
-<?php
-
-/**
- * OAuthSimple - A simpler version of OAuth
- *
- * https://github.com/jrconlin/oauthsimple
- *
- * @author     jr conlin <src@jrconlin.com>
- * @copyright  unitedHeroes.net 2011
- * @version    1.3
- * @license    BSD licence.
- */
-class OAuthSimple
-{
-    private $_secrets;
-    private $_default_signature_method;
-    private $_action;
-    private $_nonce_chars;
-
-    /**
-     * OAuthSimple constructor.
-     *
-     * @param string $APIKey       The API Key (sometimes referred to as the consumer key). This value is usually
-     *                             supplied by the site you wish to use.
-     * @param string $sharedSecret The shared secret. This value is also usually provided by the site you wish to use.
-     *
-     * @return OAuthSimple
-     */
-    function __construct($APIKey = "", $sharedSecret = "")
-    {
-
-        if (!empty($APIKey)) {
-            $this->_secrets['consumer_key'] = $APIKey;
-        }
-
-        if (!empty($sharedSecret)) {
-            $this->_secrets['shared_secret'] = $sharedSecret;
-        }
-
-        $this->_default_signature_method = "HMAC-SHA1";
-        $this->_action = "GET";
-        $this->_nonce_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-
-        return $this;
-    }
-
-    /**
-     * Reset the parameters and URL.
-     *
-     * @return OAuthSimple
-     */
-    public function reset()
-    {
-        $this->_parameters = Array();
-        $this->path = null;
-        $this->sbs = null;
-
-        return $this;
-    }
-
-    /**
-     * Set the parameters either from a hash or a string.
-     *
-     * @param array $parameters List of parameters for the call,
-     *                          this can either be a URI string (e.g."foo=bar&gorp=banana" or an object/hash)
-     *
-     * @throws OAuthSimpleException
-     *
-     * @return OAuthSimple
-     */
-    public function setParameters($parameters = [])
-    {
-
-        if (is_string($parameters)) {
-            $parameters = $this->_parseParameterString($parameters);
-        }
-        if (empty($this->_parameters)) {
-            $this->_parameters = $parameters;
-        } elseif (!empty($parameters)) {
-            $this->_parameters = array_merge($this->_parameters, $parameters);
-        }
-        if (empty($this->_parameters['oauth_nonce'])) {
-            $this->_getNonce();
-        }
-        if (empty($this->_parameters['oauth_timestamp'])) {
-            $this->_getTimeStamp();
-        }
-        if (empty($this->_parameters['oauth_consumer_key'])) {
-            $this->_getApiKey();
-        }
-        if (empty($this->_parameters['oauth_token'])) {
-            $this->_getAccessToken();
-        }
-        if (empty($this->_parameters['oauth_signature_method'])) {
-            $this->setSignatureMethod();
-        }
-        if (empty($this->_parameters['oauth_version'])) {
-            $this->_parameters['oauth_version'] = "1.0";
-        }
-
-        return $this;
-    }
-
-    /**
-     * Convenience method for setParameters.
-     *
-     * @param array $parameters
-     *
-     * @throws OAuthSimpleException
-     *
-     * @return OAuthSimple
-     */
-    public function setQueryString($parameters)
-    {
-        return $this->setParameters($parameters);
-    }
-
-    /**
-     * Set the target URL (does not include the parameters).
-     *
-     * @param string $path The fully qualified URI (excluding query arguments) (e.g "http://example.org/foo")
-     *
-     * @throws OAuthSimpleException
-     *
-     * @return OAuthSimple
-     */
-    public function setURL($path)
-    {
-        if (empty($path)) {
-            throw new OAuthSimpleException('No path specified for OAuthSimple.setURL');
-        }
-
-        $this->_path = $path;
-
-        return $this;
-    }
-
-    /**
-     * Convenience method for setURL.
-     *
-     * @param string $path
-     *
-     * @return mixed
-     */
-    public function setPath($path)
-    {
-        return $this->_path = $path;
-    }
-
-    /**
-     * Set the "action" for the url, (e.g. GET,POST, DELETE, etc.).
-     *
-     * @param string $action HTTP Action word.
-     *
-     * @throws OAuthSimpleException
-     *
-     * @return $this
-     */
-    public function setAction($action)
-    {
-        if (empty($action)) {
-            $action = 'GET';
-        }
-        $action = strtoupper($action);
-        if (preg_match('/[^A-Z]/', $action)) {
-            throw new OAuthSimpleException('Invalid action specified for OAuthSimple.setAction');
-        }
-        $this->_action = $action;
-
-        return $this;
-    }
-
-    /**
-     * Set the signatures (as well as validate the ones you have).
-     *
-     * @param array $signatures   object/hash of the token/signature pairs {api_key:, shared_secret:, oauth_token:
-     *                            oauth_secret:}
-     *
-     * @throws OAuthSimpleException
-     *
-     * @return $this
-     */
-    public function signatures($signatures)
-    {
-        if (!empty($signatures) && !is_array($signatures)) {
-            throw new OAuthSimpleException('Must pass dictionary array to OAuthSimple.signatures');
-        }
-        if (!empty($signatures)) {
-            if (empty($this->_secrets)) {
-                $this->_secrets = Array();
-            }
-            $this->_secrets = array_merge($this->_secrets, $signatures);
-        }
-        if (isset($this->_secrets['api_key'])) {
-            $this->_secrets['consumer_key'] = $this->_secrets['api_key'];
-        }
-        if (isset($this->_secrets['access_token'])) {
-            $this->_secrets['oauth_token'] = $this->_secrets['access_token'];
-        }
-        if (isset($this->_secrets['access_secret'])) {
-            $this->_secrets['shared_secret'] = $this->_secrets['access_secret'];
-        }
-        if (isset($this->_secrets['oauth_token_secret'])) {
-            $this->_secrets['oauth_secret'] = $this->_secrets['oauth_token_secret'];
-        }
-        if (empty($this->_secrets['consumer_key'])) {
-            throw new OAuthSimpleException('Missing required consumer_key in OAuthSimple.signatures');
-        }
-        if (empty($this->_secrets['shared_secret'])) {
-            throw new OAuthSimpleException('Missing requires shared_secret in OAuthSimple.signatures');
-        }
-        if (!empty($this->_secrets['oauth_token']) && empty($this->_secrets['oauth_secret'])) {
-            throw new OAuthSimpleException('Missing oauth_secret for supplied oauth_token in OAuthSimple.signatures');
-        }
-
-        return $this;
-    }
-
-    /**
-     * @param array $signatures
-     *
-     * @throws OAuthSimpleException
-     *
-     * @return OAuthSimple
-     */
-    public function setTokensAndSecrets($signatures)
-    {
-        return $this->signatures($signatures);
-    }
-
-    /**
-     * Set the signature method (currently only Plaintext or SHA-MAC1).
-     *
-     * @param string $method Method of signing the transaction (only PLAINTEXT and SHA-MAC1 allowed for now).
-     *
-     * @throws OAuthSimpleException
-     *
-     * @return $this
-     */
-    public function setSignatureMethod($method = "")
-    {
-        if (empty($method)) {
-            $method = $this->_default_signature_method;
-        }
-        $method = strtoupper($method);
-        switch ($method) {
-            case 'PLAINTEXT':
-            case 'HMAC-SHA1':
-                $this->_parameters['oauth_signature_method'] = $method;
-                break;
-            default:
-                throw new OAuthSimpleException (
-                    "Unknown signing method $method specified for OAuthSimple.setSignatureMethod"
-                );
-                break;
-        }
-
-        return $this;
-    }
-
-    /**
-     * Sign the request.
-     *
-     * note: all arguments are optional, provided you've set them using the
-     * other helper functions.
-     *
-     * @param array $args Optional.
-     *                    Hash of arguments for the call {action, path, parameters (array), method, signatures, (array)}
-     *
-     * @throws OAuthSimpleException
-     *
-     * @return array
-     */
-    public function sign($args = array())
-    {
-        if (!empty($args['action'])) {
-            $this->setAction($args['action']);
-        }
-        if (!empty($args['path'])) {
-            $this->setPath($args['path']);
-        }
-        if (!empty($args['method'])) {
-            $this->setSignatureMethod($args['method']);
-        }
-        if (!empty($args['signatures'])) {
-            $this->signatures($args['signatures']);
-        }
-        if (empty($args['parameters'])) {
-            $args['parameters'] = array();
-        }
-        $this->setParameters($args['parameters']);
-        $normParams = $this->_normalizedParameters();
-
-        return Array(
-            'parameters' => $this->_parameters,
-            'signature' => self::_oauthEscape($this->_parameters['oauth_signature']),
-            'signed_url' => $this->_path.'?'.$normParams,
-            'header' => $this->getHeaderString(),
-            'sbs' => $this->sbs,
-        );
-    }
-
-    /**
-     * Return a formatted "header" string.
-     *
-     * NOTE: This doesn't set the "Authorization: " prefix, which is required.
-     * It's not set because various set header functions prefer different
-     * ways to do that.
-     *
-     * @param array $args
-     *
-     * @throws OAuthSimpleException
-     *
-     * @return null|string|string[]
-     */
-    public function getHeaderString($args = array())
-    {
-        if (empty($this->_parameters['oauth_signature'])) {
-            $this->sign($args);
-        }
-        $result = 'OAuth ';
-
-        foreach ($this->_parameters as $pName => $pValue) {
-            if (strpos($pName, 'oauth_') !== 0) {
-                continue;
-            }
-            if (is_array($pValue)) {
-                foreach ($pValue as $val) {
-                    $result .= $pName.'="'.self::_oauthEscape($val).'", ';
-                }
-            } else {
-                $result .= $pName.'="'.self::_oauthEscape($pValue).'", ';
-            }
-        }
-
-        return preg_replace('/, $/', '', $result);
-    }
-
-    /**
-     * @param string $paramString
-     *
-     * @return array
-     */
-    private function _parseParameterString($paramString)
-    {
-        $elements = explode('&', $paramString);
-        $result = array();
-        foreach ($elements as $element) {
-            list ($key, $token) = explode('=', $element);
-            if ($token) {
-                $token = urldecode($token);
-            }
-            if (!empty($result[$key])) {
-                if (!is_array($result[$key])) {
-                    $result[$key] = array($result[$key], $token);
-                } else {
-                    array_push($result[$key], $token);
-                }
-            } else {
-                $result[$key] = $token;
-            }
-        }
-
-        return $result;
-    }
-
-    /**
-     * @param string $string
-     *
-     * @throws OAuthSimpleException
-     *
-     * @return int|mixed|string
-     */
-    private static function _oauthEscape($string)
-    {
-        if ($string === 0) {
-            return 0;
-        }
-        if ($string == '0') {
-            return '0';
-        }
-        if (strlen($string) == 0) {
-            return '';
-        }
-        if (is_array($string)) {
-            throw new OAuthSimpleException('Array passed to _oauthEscape');
-        }
-        $string = urlencode($string);
-
-        //FIX: urlencode of ~ and '+'
-        $string = str_replace(
-            Array('%7E', '+'), // Replace these
-            Array('~', '%20'), // with these
-            $string
-        );
-
-        return $string;
-    }
-
-    /**
-     * @param int $length
-     *
-     * @return string
-     */
-    private function _getNonce($length = 5)
-    {
-        $result = '';
-        $cLength = strlen($this->_nonce_chars);
-        for ($i = 0; $i < $length; $i++) {
-            $rnum = rand(0, $cLength - 1);
-            $result .= substr($this->_nonce_chars, $rnum, 1);
-        }
-        $this->_parameters['oauth_nonce'] = $result;
-
-        return $result;
-    }
-
-    /**
-     * @throws OAuthSimpleException
-     *
-     * @return mixed
-     */
-    private function _getApiKey()
-    {
-        if (empty($this->_secrets['consumer_key'])) {
-            throw new OAuthSimpleException('No consumer_key set for OAuthSimple');
-        }
-        $this->_parameters['oauth_consumer_key'] = $this->_secrets['consumer_key'];
-
-        return $this->_parameters['oauth_consumer_key'];
-    }
-
-    /**
-     * @throws OAuthSimpleException
-     *
-     * @return string
-     */
-    private function _getAccessToken()
-    {
-        if (!isset($this->_secrets['oauth_secret'])) {
-            return '';
-        }
-        if (!isset($this->_secrets['oauth_token'])) {
-            throw new OAuthSimpleException('No access token (oauth_token) set for OAuthSimple.');
-        }
-        $this->_parameters['oauth_token'] = $this->_secrets['oauth_token'];
-
-        return $this->_parameters['oauth_token'];
-    }
-
-    /**
-     * @return int
-     */
-    private function _getTimeStamp()
-    {
-        return $this->_parameters['oauth_timestamp'] = time();
-    }
-
-    /**
-     * @throws OAuthSimpleException
-     *
-     * @return string
-     */
-    private function _normalizedParameters()
-    {
-        $normalized_keys = array();
-        $return_array = array();
-
-        foreach ($this->_parameters as $paramName => $paramValue) {
-            if (preg_match('/w+_secret/', $paramName) OR
-                $paramName == "oauth_signature") {
-                continue;
-            }
-            // Read parameters from a file. Hope you're practicing safe PHP.
-            //if (strpos($paramValue, '@') !== 0 && !file_exists(substr($paramValue, 1)))
-            //{
-            if (is_array($paramValue)) {
-                $normalized_keys[self::_oauthEscape($paramName)] = array();
-                foreach ($paramValue as $item) {
-                    array_push($normalized_keys[self::_oauthEscape($paramName)], self::_oauthEscape($item));
-                }
-            } else {
-                $normalized_keys[self::_oauthEscape($paramName)] = self::_oauthEscape($paramValue);
-            }
-            //}
-        }
-
-        ksort($normalized_keys);
-
-        foreach ($normalized_keys as $key => $val) {
-            if (is_array($val)) {
-                sort($val);
-                foreach ($val as $element) {
-                    array_push($return_array, $key."=".$element);
-                }
-            } else {
-                array_push($return_array, $key.'='.$val);
-            }
-
-        }
-        $presig = join("&", $return_array);
-        $sig = $this->_generateSignature($presig);
-        $this->_parameters['oauth_signature'] = $sig;
-        array_push($return_array, "oauth_signature=$sig");
-
-        return join("&", $return_array);
-    }
-
-    /**
-     * @param string $parameters
-     *
-     * @throws OAuthSimpleException
-     *
-     * @return string
-     */
-    private function _generateSignature($parameters = "")
-    {
-        $secretKey = '';
-        if (isset($this->_secrets['shared_secret'])) {
-            $secretKey = self::_oauthEscape($this->_secrets['shared_secret']);
-        }
-
-        $secretKey .= '&';
-        if (isset($this->_secrets['oauth_secret'])) {
-            $secretKey .= self::_oauthEscape($this->_secrets['oauth_secret']);
-        }
-        if (!empty($parameters)) {
-            $parameters = urlencode($parameters);
-        }
-        switch ($this->_parameters['oauth_signature_method']) {
-            case 'PLAINTEXT':
-                return urlencode($secretKey);;
-            case 'HMAC-SHA1':
-                $this->sbs = self::_oauthEscape($this->_action).'&'.self::_oauthEscape($this->_path).'&'.$parameters;
-
-                return base64_encode(hash_hmac('sha1', $this->sbs, $secretKey, true));
-            default:
-                throw new OAuthSimpleException('Unknown signature method for OAuthSimple');
-                break;
-        }
-    }
-
-    /**
-     * @param $string
-     *
-     * @return string
-     */
-    public static function generateBodyHash($string)
-    {
-        $hash = sha1($string, true);
-
-        return base64_encode($hash);
-    }
-
-    /**
-     * @param string $authorizationHeader
-     *
-     * @return array
-     */
-    public static function getAuthorizationParams($authorizationHeader)
-    {
-        if ('OAuth ' !== substr($authorizationHeader, 0, 6)) {
-            return [];
-        }
-
-        $params = [];
-        $authString = str_replace('OAuth ', '', $authorizationHeader);
-        $authParts = explode(',', $authString);
-
-        foreach ($authParts as $authPart) {
-            list($key, $value) = explode('=', $authPart, 2);
-
-            $key = trim($key);
-            $value = trim($value, " \"");
-
-            $params[$key] = urldecode($value);
-        }
-
-        return $params;
-    }
-}
-
-/**
- * Class OAuthSimpleException.
- */
-class OAuthSimpleException extends Exception
-{
-    /**
-     * OAuthSimpleException constructor.
-     *
-     * @param string $err
-     * @param bool   $isDebug
-     */
-    public function __construct($err, $isDebug = false)
-    {
-        self::log_error($err);
-        if ($isDebug) {
-            self::display_error($err, true);
-        }
-    }
-
-    /**
-     * @param string $err
-     */
-    public static function log_error($err)
-    {
-        error_log($err, 0);
-    }
-
-    /**
-     * @param string $err
-     * @param bool   $kill
-     */
-    public static function display_error($err, $kill = false)
-    {
-        print_r($err);
-        if ($kill === false) {
-            die();
-        }
-    }
-}

+ 7 - 1
plugin/ims_lti/README.md

@@ -1,7 +1,7 @@
 IMS/LTI plugin
 ===
 
-Version 1.4 (beta)
+Version 1.5 (beta)
 
 This plugin is meant to be later integrated into Chamilo (in a major version
 release).
@@ -37,6 +37,12 @@ external tool.
 **v1.4**
 * Allow create external tools when there is no key/secret available for launch
 
+**v1.5**
+* Plugin has passed the tests from the LTI Certification suite.
+* Add support for substitution of variable.
+  See `ImsLti::getSubstitutableParams()`.
+* Outcome services has a unique URL and sourced ID.
+
 # Installation
 
 1. Install the plugin from Plugin page

+ 0 - 1
plugin/ims_lti/form.php

@@ -7,7 +7,6 @@ use Chamilo\PluginBundle\Entity\ImsLti\ImsLtiTool;
 use Chamilo\UserBundle\Entity\User;
 
 require_once __DIR__.'/../../main/inc/global.inc.php';
-require './OAuthSimple.php';
 
 api_protect_course_script(false);
 api_block_anonymous_users(false);

+ 7 - 19
plugin/ims_lti/item_return.php

@@ -26,29 +26,17 @@ if (!$ltiTool) {
     api_not_allowed();
 }
 
-$oauth = new OAuthSimple(
+$consumer = new OAuthConsumer(
     $_POST['oauth_consumer_key'],
     $ltiTool->getSharedSecret()
 );
-$oauth->setAction('POST');
-$oauth->setSignatureMethod($_POST['oauth_signature_method']);
-$result = $oauth->sign(
-    [
-        'path' => api_get_path(WEB_PLUGIN_PATH).'ims_lti/item_return.php',
-        'parameters' => [
-            'content_items' => $_POST['content_items'],
-            'data' => $_POST['data'],
-            'lti_version' => $_POST['lti_version'],
-            'lti_message_type' => $_POST['lti_message_type'],
-            'oauth_nonce' => $_POST['oauth_nonce'],
-            'oauth_timestamp' => $_POST['oauth_timestamp'],
-            'oauth_signature_method' => $_POST['oauth_signature_method'],
-            'oauth_callback' => $_POST['oauth_callback'],
-        ],
-    ]
-);
+$hmacMethod = new OAuthSignatureMethod_HMAC_SHA1();
+
+$request = OAuthRequest::from_request('POST', api_get_path(WEB_PLUGIN_PATH).'ims_lti/item_return.php');
+$request->sign_request($hmacMethod, $consumer, '');
+$signature = $request->get_parameter('oauth_signature');
 
-if ($result['parameters']['oauth_signature'] !== $_POST['oauth_signature']) {
+if ($signature !== $_POST['oauth_signature']) {
     api_not_allowed();
 }
 

+ 0 - 1
plugin/ims_lti/outcome_service.php

@@ -4,7 +4,6 @@
 use Chamilo\PluginBundle\Entity\ImsLti\ImsLtiTool;
 
 require_once __DIR__.'/../../main/inc/global.inc.php';
-require_once './OAuthSimple.php';
 
 header('Content-Type: application/xml');
 

BIN
plugin/notebookteacher/resources/img/32/notebookteacher_na.png


+ 17 - 12
plugin/notebookteacher/src/NotebookTeacherPlugin.php

@@ -58,18 +58,23 @@ class NotebookTeacherPlugin extends Plugin
             return false;
         }
 
-        $srcfile1 = __DIR__.'/../resources/img/64/notebookteacher.png';
-        $srcfile2 = __DIR__.'/../resources/img/64/notebookteacher_na.png';
-        $srcfile3 = __DIR__.'/../resources/img/32/notebookteacher.png';
-        $srcfile4 = __DIR__.'/../resources/img/22/notebookteacher.png';
-        $dstfile1 = __DIR__.'/../../../main/img/icons/64/notebookteacher.png';
-        $dstfile2 = __DIR__.'/../../../main/img/icons/64/notebookteacher_na.png';
-        $dstfile3 = __DIR__.'/../../../main/img/icons/32/notebookteacher.png';
-        $dstfile4 = __DIR__.'/../../../main/img/notebookteacher.png';
-        copy($srcfile1, $dstfile1);
-        copy($srcfile2, $dstfile2);
-        copy($srcfile3, $dstfile3);
-        copy($srcfile4, $dstfile4);
+        $list = [
+            '/64/notebookteacher.png',
+            '/64/notebookteacher_na.png',
+            '/32/notebookteacher.png',
+            '/32/notebookteacher_na.png',
+            '/32/test2pdf_na.png',
+            '/22/notebookteacher.png',
+        ];
+
+        foreach ($list as $file) {
+            $source = __DIR__.'/../resources/img/'.$file;
+            $destination = __DIR__.'/../../../main/img/icons/'.$file;
+            $res = @copy($source, $destination);
+            if (!$res) {
+                break;
+            }
+        }
 
         require_once api_get_path(SYS_PLUGIN_PATH).'notebookteacher/database.php';
     }

BIN
plugin/test2pdf/resources/img/32/test2pdf.png


BIN
plugin/test2pdf/resources/img/32/test2pdf_na.png


+ 22 - 13
plugin/test2pdf/src/test2pdf_plugin.class.php

@@ -39,19 +39,28 @@ class Test2pdfPlugin extends Plugin
         //Installing course settings
         $this->install_course_fields_in_all_courses();
 
-        $srcfile1 = __DIR__.'/../resources/img/64/test2pdf.png';
-        $srcfile2 = __DIR__.'/../resources/img/64/test2pdf_na.png';
-        $srcfile3 = __DIR__.'/../resources/img/22/test2pdf.png';
-        $dstfile1 = __DIR__.'/../../../main/img/icons/64/test2pdf.png';
-        $dstfile2 = __DIR__.'/../../../main/img/icons/64/test2pdf_na.png';
-        $dstfile3 = __DIR__.'/../../../main/img/test2pdf.png';
-        $res1 = @copy($srcfile1, $dstfile1);
-        $res2 = @copy($srcfile2, $dstfile2);
-        $res3 = @copy($srcfile3, $dstfile3);
-        if (!$res1 || !$res2 || !$res3) {
-            $warning = 'Test2PDF plugin icons could not be copied to main/img/ because of folder permissions. To fix, give web server user permissions to write to main/img/ before enabling this plugin.';
-            Display::addFlash($warning);
-            error_log($warning);
+        $list = [
+            '/64/test2pdf.png',
+            '/64/test2pdf_na.png',
+            '/32/test2pdf.png',
+            '/32/test2pdf_na.png',
+            '/22/test2pdf.png',
+        ];
+
+        $res = true;
+        foreach ($list as $file) {
+            $source = __DIR__.'/../resources/img/'.$file;
+            $destination = __DIR__.'/../../../main/img/icons/'.$file;
+            $res = @copy($source, $destination);
+            if (!$res) {
+                break;
+            }
+        }
+
+        if (!$res) {
+            $warning = 'Test2PDF plugin icons could not be copied to main/img/ because of folder permissions. 
+            To fix, give web server user permissions to write to main/img/ before enabling this plugin.';
+            Display::addFlash(Display::return_message($warning, 'warning'));
         }
     }
 

+ 3 - 1
src/Chamilo/CourseBundle/Component/CourseCopy/CourseBuilder.php

@@ -1144,7 +1144,9 @@ class CourseBuilder
                 $obj->invited,
                 $obj->answered,
                 $obj->invite_mail,
-                $obj->reminder_mail
+                $obj->reminder_mail,
+                $obj->one_question_per_page,
+                $obj->shuffle
             );
             $sql = 'SELECT * FROM '.$table_question.'
                     WHERE c_id = '.$courseId.' AND survey_id = '.$obj->survey_id;

+ 4 - 2
src/Chamilo/CourseBundle/Component/CourseCopy/CourseRestorer.php

@@ -2354,14 +2354,16 @@ class CourseRestorer
                     'avail_till' => self::DBUTF8($survey->avail_till),
                     'is_shared' => self::DBUTF8($survey->is_shared),
                     'template' => self::DBUTF8($survey->template),
-                    'intro' => ($survey->intro === false ? '' : self::DBUTF8($survey->intro)),
-                    'surveythanks' => ($survey->surveythanks === false ? '' : self::DBUTF8($survey->surveythanks)),
+                    'intro' => $survey->intro === false ? '' : self::DBUTF8($survey->intro),
+                    'surveythanks' => $survey->surveythanks === false ? '' : self::DBUTF8($survey->surveythanks),
                     'creation_date' => self::DBUTF8($survey->creation_date),
                     'invited' => '0',
                     'answered' => '0',
                     'invite_mail' => self::DBUTF8($survey->invite_mail),
                     'reminder_mail' => self::DBUTF8($survey->reminder_mail),
                     'session_id' => $sessionId,
+                    'one_question_per_page' => isset($survey->one_question_per_page) ? $survey->one_question_per_page : 0,
+                    'shuffle' => isset($survey->suffle) ? $survey->suffle : 0,
                 ];
 
                 // An existing survey exists with the same code and the same language

+ 7 - 1
src/Chamilo/CourseBundle/Component/CourseCopy/Resources/Survey.php

@@ -92,6 +92,8 @@ class Survey extends Resource
      * @param int    $answered
      * @param string $invite_mail
      * @param string $reminder_mail
+     * @param int    $oneQuestionPerPage
+     * @param int    $shuffle
      */
     public function __construct(
         $id,
@@ -110,7 +112,9 @@ class Survey extends Resource
         $invited,
         $answered,
         $invite_mail,
-        $reminder_mail
+        $reminder_mail,
+        $oneQuestionPerPage,
+        $shuffle
     ) {
         parent::__construct($id, RESOURCE_SURVEY);
         $this->code = $code;
@@ -131,6 +135,8 @@ class Survey extends Resource
         $this->reminder_mail = $reminder_mail;
         $this->question_ids = [];
         $this->invitation_ids = [];
+        $this->one_question_per_page = $oneQuestionPerPage;
+        $this->shuffle = $shuffle;
     }
 
     /**

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません