Browse Source

Minor - Merge from 1.11.x

jmontoyaa 7 years ago
parent
commit
98ef2b54cb
92 changed files with 1960 additions and 1307 deletions
  1. 84 1
      documentation/changelog.html
  2. 11 1
      documentation/optimization.html
  3. 2 1
      documentation/security.html
  4. 0 1
      main/admin/skill.php
  5. 3 5
      main/admin/skill_badge_create.php
  6. 86 0
      main/admin/skill_rel_course.php
  7. 7 7
      main/course_home/course_home.php
  8. 7 0
      main/course_info/infocours.php
  9. 0 1
      main/course_progress/thematic_controller.php
  10. 0 4
      main/document/document.php
  11. 16 21
      main/exercise/exercise.class.php
  12. 2 6
      main/exercise/exercise_show.php
  13. 1 5
      main/exercise/hotpotatoes_exercise_result.class.php
  14. 0 6
      main/exercise/upload_exercise.php
  15. 0 25
      main/forum/forumfunction.inc.php
  16. 98 29
      main/glossary/index.php
  17. 1 1
      main/gradebook/gradebook_display_summary.php
  18. 32 11
      main/gradebook/index.php
  19. 4 0
      main/gradebook/lib/be/abstractlink.class.php
  20. 3 14
      main/gradebook/lib/be/category.class.php
  21. 60 32
      main/gradebook/lib/fe/gradebooktable.class.php
  22. 19 14
      main/gradebook/lib/gradebook_data_generator.class.php
  23. 6 5
      main/gradebook/lib/gradebook_result.class.php
  24. 87 0
      main/gradebook/skill_rel_user.php
  25. 13 17
      main/inc/ajax/announcement.ajax.php
  26. 1 0
      main/inc/ajax/document.ajax.php
  27. 9 3
      main/inc/ajax/exercise.ajax.php
  28. 16 7
      main/inc/ajax/extra_field.ajax.php
  29. 27 3
      main/inc/ajax/lp.ajax.php
  30. 6 1
      main/inc/ajax/model.ajax.php
  31. 107 18
      main/inc/ajax/skill.ajax.php
  32. 7 8
      main/lp/aiccItem.class.php
  33. 1 0
      main/lp/aicc_hacp.php
  34. 153 183
      main/lp/learnpath.class.php
  35. 143 150
      main/lp/learnpathItem.class.php
  36. 0 1
      main/lp/lp_add_audio.php
  37. 9 16
      main/lp/lp_ajax_initialize.php
  38. 3 1
      main/lp/lp_ajax_save_item.php
  39. 1 15
      main/lp/lp_ajax_start_timer.php
  40. 8 5
      main/lp/lp_ajax_switch_item.php
  41. 1 0
      main/lp/lp_ajax_switch_item_toc.php
  42. 8 6
      main/lp/lp_content.php
  43. 54 50
      main/lp/lp_controller.php
  44. 9 9
      main/lp/lp_list.php
  45. 6 9
      main/lp/lp_nav.php
  46. 0 2
      main/lp/lp_subscribe_users_to_category.php
  47. 79 0
      main/lp/lp_update_scorm.php
  48. 14 11
      main/lp/lp_view.php
  49. 0 1
      main/lp/openoffice_text_document.class.php
  50. 1 1
      main/lp/scorm_api.php
  51. 6 1
      main/mySpace/exercise_category_report.php
  52. 23 6
      main/mySpace/myStudents.php
  53. 18 20
      main/notebook/index.php
  54. 22 13
      main/session/resume_session.php
  55. 13 3
      main/session/scheduled_announcement.php
  56. 90 15
      main/session/session_add.php
  57. 2 2
      main/session/session_list.php
  58. 2 1
      main/social/home.php
  59. 1 1
      main/social/my_skills_report.php
  60. 3 2
      main/social/search.php
  61. 8 6
      main/survey/create_new_survey.php
  62. 8 4
      main/survey/fillsurvey.php
  63. 59 0
      main/survey/pending.php
  64. 2 3
      main/survey/question.php
  65. 2 3
      main/survey/reporting.php
  66. 2 3
      main/survey/survey.download.inc.php
  67. 19 22
      main/survey/survey.lib.php
  68. 109 47
      main/survey/surveyUtil.class.php
  69. 4 2
      main/ticket/assign_tickets.php
  70. 1 1
      main/ticket/new_ticket.php
  71. 2 1
      main/tracking/course_log_events.php
  72. 12 11
      main/tracking/course_log_groups.php
  73. 2 1
      main/tracking/course_log_resources.php
  74. 2 1
      main/tracking/course_log_tools.php
  75. 77 0
      main/tracking/messages.php
  76. 10 10
      main/upload/form.document.php
  77. 0 38
      main/upload/form.scorm.php
  78. 7 1
      main/upload/upload_ppt.php
  79. 13 4
      main/upload/upload_word.php
  80. 6 11
      main/user/add_users_to_session.php
  81. 2 2
      main/user/classes.php
  82. 1 2
      main/user/resume_session.php
  83. 12 3
      main/work/add_user.php
  84. 3 4
      main/work/download.php
  85. 18 15
      main/work/downloadfolder.inc.php
  86. 6 3
      main/work/upload_corrections.php
  87. 85 312
      main/work/work.lib.php
  88. 2 2
      main/work/work.php
  89. 92 17
      main/work/work_list.php
  90. 2 3
      main/work/work_list_all.php
  91. 3 2
      main/work/work_list_others.php
  92. 4 2
      main/work/work_missing.php

+ 84 - 1
documentation/changelog.html

@@ -111,6 +111,29 @@
     <table class="table" id="index">
 
     </table>
+    <div class="version" aria-role="article" aria-label="1.11.8">
+    <a name="1.11.8"></a>
+
+    <h1>Chamilo 1.11.8 - ??????,  ??/??/????</h1>
+    <h3>Release notes - summary</h3>
+    <p>Chamilo 1.11.8 is a minor, bugfix release on top of 1.11.6.</p>
+    <h3>Release name</h3>
+        <p>?????????????</p>
+        <h3>Security fixes</h3>
+        <h3>Possibly breaking changes</h3>
+        <h3>Notable new Features</h3>
+        <h4>For end-users, teachers and Chamilo admins</h4>
+        <h4>For developers and sysadmins</h4>
+        <ul>
+            <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/604950ff544088ee30632b9a85857cfee2adbc1d">604950ff</a>)  Don't clear class for audio/video when removing xss - refs BT#14026 </li>
+        </ul>
+        <h3>Improvements (minor features) and debug</h3>
+        <h3>Stylesheets and theming</h3>
+        <h3>Web services</h3>
+        <h3>Removals</h3>
+        <h3>Known issues</h3>
+    </div>
+
     <div class="version" aria-role="article" aria-label="1.11.6">
     <a name="1.11.6"></a>
 
@@ -127,9 +150,10 @@
     <ul aria-role="log" aria-live="off">
     </ul>
 
-    <h3>Notable new Features</h3>
+    <h3>Notable new Features</h3>.
     <h4>For end-users, teachers and Chamilo admins</h4>
     <ul aria-role="log" aria-live="off">
+        <li>[2018-01-17] (<a href="https://github.com/chamilo/chamilo-lms/commit/49ba4f6d7b9888928e19a19b331d37925d86c351">49ba4f6d</a>) Plugin: Test2PDF: Add test2pdf plugin to convert exercises to PDF. This plugin can be further cleaned up but works as is.</li>
         <li>[2017-12-28] (<a href="https://github.com/chamilo/chamilo-lms/commit/958f1f59b64e4008915350cadaf1e4fb3370f578">958f1f59</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2229">GH#2229</a>) Document: Add Cloud Files links (and fixes)</li>
         <li>[2017-10-16] (<a href="https://github.com/chamilo/chamilo-lms/commit/8700571d786e7d153479bd43b1744676cd75daaf">8700571d</a>) Document: Add webm support in showinframes.php (using jplayer)</li>
         <li>[2017-10-10] (<a href="https://github.com/chamilo/chamilo-lms/commit/5039c7b294a15f1109308d2a0a2e53f043b5113b">5039c7b2</a> - <a href="https://task.beeznest.com/issues/13527">BT#13527</a>) Learnpath: Add pdf export button in LP result page</li>
@@ -205,6 +229,65 @@
 
     <h3>Improvements (minor features) and debug</h3>
     <ul aria-role="log" aria-live="off">
+        <li>[2018-01-18] (<a href="https://github.com/chamilo/chamilo-lms/commit/d713cbff9ff1ebceb4c0e90ecb8131bcf04763cd">d713cbff</a>) Add LTI plugin translation to French and Spanish</li>
+        <li>[2018-01-18] (<a href="https://github.com/chamilo/chamilo-lms/commit/002b907867dcea704c0533b85c3876a91e393e97">002b9078</a> - <a href="https://task.beeznest.com/issues/13469">BT#13469</a>) Fix and improve Ims/LIT plugin</li>
+        <li>[2018-01-18] (<a href="https://github.com/chamilo/chamilo-lms/commit/e40687261466e8b5a8173db9e62c7c5faa3b5d97">e4068726</a>) Style: Fix dropdown menu color</li>
+        <li>[2018-01-18] (<a href="https://github.com/chamilo/chamilo-lms/commit/20482534a3e640f7170949786e9f56b83cd23851">20482534</a> - <a href="https://task.beeznest.com/issues/13885">BT#13885</a>) Chat: Fix Send button in course chat</li>
+        <li>[2018-01-18] (<a href="https://github.com/chamilo/chamilo-lms/commit/dc70f282cb372a77d4d60abdfce39adcab4187bf">dc70f282</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2343">GH#2343</a>) Quiz: Fix question pool when filter by session using api_get_session_condition</li>
+        <li>[2018-01-18] (<a href="https://github.com/chamilo/chamilo-lms/commit/8b9e8517858387e63a59a2fa1228e55572299b97">8b9e8517</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2268">GH#2268</a>) Display: Fix course progress invisible by default bug</li>
+        <li>[2018-01-18] (<a href="https://github.com/chamilo/chamilo-lms/commit/ae5a2c11c2349515d8fb83c2f7a7625757c2a3c1">ae5a2c11</a> - <a href="https://task.beeznest.com/issues/13886">BT#13886</a>) Internal: Fix URL redirection when saving course settings</li>
+        <li>[2018-01-18] (<a href="https://github.com/chamilo/chamilo-lms/commit/535f3815a5baddecb1af915083d04f155576ffcd">535f3815</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2341">GH#2341</a>) Gradebook: Add certificate=true in links</li>
+        <li>[2018-01-17] (<a href="https://github.com/chamilo/chamilo-lms/commit/246dfdcfc9ad302bb61d910aefe68cbb46d470fe">246dfdcf</a> - <a href="https://task.beeznest.com/issues/13885">BT#13885</a>) Chat: Tutors only can chat with students when course_chat_restrict_to_coach is enabled</li>
+        <li>[2018-01-17] (<a href="https://github.com/chamilo/chamilo-lms/commit/f180a4c98bc68187482d959394387359c5d10b49">f180a4c9</a> - <a href="https://task.beeznest.com/issues/13887">BT#13887</a>) Skill : Remove . from language variable names to avoid breakdown</li>
+        <li>[2018-01-17] (<a href="https://github.com/chamilo/chamilo-lms/commit/29465ed4b1770ecd37a79bf5e65b43f0d6379a98">29465ed4</a> - <a href="https://task.beeznest.com/issues/13885">BT#13885</a>) Chat: Add option to restrict course chat only for coaches</li>
+        <li>[2018-01-17] (<a href="https://github.com/chamilo/chamilo-lms/commit/49bb2540d9a3b69649e4ed8bc724d5fb5a5270ad">49bb2540</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2214">GH#2214</a>) Install: Fix issue in update process not removing deprecated paths</li>
+        <li>[2018-01-17] (<a href="https://github.com/chamilo/chamilo-lms/commit/5c2549a6d945162a797963d93335736dfe530ab1">5c2549a6</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2214">GH#2214</a>) Instlal: Allow continue installation if deprecated folder exists - Folders will be deleted afterwards.</li>
+        <li>[2018-01-17] (<a href="https://github.com/chamilo/chamilo-lms/commit/dd3e3e1a0275d57fa34f8048558978eea51c35a3">dd3e3e1a</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2332">GH#2332</a>) Gradebook: Fix generated certificate redirection</li>
+        <li>[2018-01-17] (<a href="https://github.com/chamilo/chamilo-lms/commit/342c7efd7a4d768d337520d7cd7f128fd2b378b7">342c7efd</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2333">GH#2333</a>) Gradebook: Fix edit document, content not updated</li>
+        <li>[2018-01-17] (<a href="https://github.com/chamilo/chamilo-lms/commit/338161b1ac87967012c4ecfe327da86b3809b314">338161b1</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2306">GH#2306</a>) Admin: Fix filter by session in course list</li>
+        <li>[2018-01-17] (<a href="https://github.com/chamilo/chamilo-lms/commit/af0cbeb0adf18bd022e93c3324eb4add7274c623">af0cbeb0</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2269">GH#2269</a>) Forum: Fix wrong forum link when using it inside a course group</li>
+        <li>[2018-01-17] (<a href="https://github.com/chamilo/chamilo-lms/commit/1d5c7a35b345264be3694afaeea03c34959223db">1d5c7a35</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2268">GH#2268</a>) Admin: Hide tool shortcut when it's hidden from teachers</li>
+        <li>[2018-01-16] (<a href="https://github.com/chamilo/chamilo-lms/commit/d9e1148e4de300f6bb333a0a4a7d3fc508436b71">d9e1148e</a> - <a href="https://task.beeznest.com/issues/13856">BT#13856</a>) Fix export PDF from HTML with /main/img paths</li>
+        <li>[2018-01-16] (<a href="https://github.com/chamilo/chamilo-lms/commit/48b85ad6dc01bc61cca608c7a1430b9a0111c560">48b85ad6</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2331">GH#2331</a>) Survey: Fix issue with URL params' "&" filtering</li>
+        <li>[2018-01-16] (<a href="https://github.com/chamilo/chamilo-lms/commit/984291c44913939311b37ce885d32f0213d8f65a">984291c4</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2331">GH#2331</a>) Survey: Fix issue updating all users from survey profile form</li>
+        <li>[2018-01-16] (<a href="https://github.com/chamilo/chamilo-lms/commit/1444f088639aa378b1e2718e274336974cf7eed6">1444f088</a>) Internal: Update user_id to id in user profile edition</li>
+        <li>[2018-01-16] (<a href="https://github.com/chamilo/chamilo-lms/commit/5695089790c37f2886d690d1d77c6c25255b3286">56950897</a> - <a href="https://task.beeznest.com/issues/13872">BT#13872</a>) Display: Improve title/tooltip for date and datetime pickers</li>
+        <li>[2018-01-16] (<a href="https://github.com/chamilo/chamilo-lms/commit/f5870090043667be95f36793a98486b694d46a92">f5870090</a> - <a href="https://task.beeznest.com/issues/13870">BT#13870</a>) Survey: Add id_session when sending link in survey</li>
+        <li>[2018-01-16] (<a href="https://github.com/chamilo/chamilo-lms/commit/5a21e6c568defc12835dc228901e17dd1bc2c47f">5a21e6c5</a> - <a href="https://task.beeznest.com/issues/13870">BT#13870</a>) Survey: Fix php notice + fix "additional email" sent in survey</li>
+        <li>[2018-01-16] (<a href="https://github.com/chamilo/chamilo-lms/commit/eb2eb66d6882b2ad12f42e8df9b3462f8a72456f">eb2eb66d</a> - <a href="https://task.beeznest.com/issues/13870">BT#13870</a>) Survey: Fix form link, to avoid "not allowed" message in fill survey</li>
+        <li>[2018-01-16] (<a href="https://github.com/chamilo/chamilo-lms/commit/ecd72c7b6f1ea9c81052e38836af465ab6f4dbae">ecd72c7b</a> - <a href="https://task.beeznest.com/issues/13870">BT#13870</a>) Survey: Fix Fatal error: Uncaught Error: [] operator not supported for strings</li>
+        <li>[2018-01-16] (<a href="https://github.com/chamilo/chamilo-lms/commit/69bb131699127c687f7713343fb9f77410c40a9c">69bb1316</a> - <a href="https://task.beeznest.com/issues/13870">BT#13870</a>) Survey: Fix get session id when getting survey</li>
+        <li>[2018-01-15] (<a href="https://github.com/chamilo/chamilo-lms/commit/a3e18eed59419cd965bec9132b4813b79be9de14">a3e18eed</a> - <a href="https://task.beeznest.com/issues/13469">BT#13469</a>) Plugin: WIP - Improving LTI plugin</li>
+        <li>[2018-01-15] (<a href="https://github.com/chamilo/chamilo-lms/commit/16d13b7a070c6041642fed2c9854a4e4f215f8e7">16d13b7a</a> - <a href="https://task.beeznest.com/issues/13872">BT#13872</a>) Display: Change icon from datetime and date pickers</li>
+        <li>[2018-01-15] (<a href="https://github.com/chamilo/chamilo-lms/commit/4cc1ce0570bdb26233707d97aa0a9ba9784adf78">4cc1ce05</a> - <a href="https://task.beeznest.com/issues/13870">BT#13870</a>) Survey: Add session id in URL to fix survey error</li>
+        <li>[2018-01-15] (<a href="https://github.com/chamilo/chamilo-lms/commit/e7f7264248672f97282dc597a1319d9c7fee4798">e7f72642</a> - <a href="https://task.beeznest.com/issues/13870">BT#13870</a>) Survey: Fix fill survey when no invitation sent</li>
+        <li>[2018-01-12] (<a href="https://github.com/chamilo/chamilo-lms/commit/2a4f588a9326b6a3b826da0a99be7006ea66bef4">2a4f588a</a>) Display: Fix kiddy CSS</li>
+        <li>[2018-01-12] (<a href="https://github.com/chamilo/chamilo-lms/commit/fdbf575e43ee88159258d709711fec1c246e48df">fdbf575e</a> - <a href="https://task.beeznest.com/issues/13868">BT#13868</a>) Quiz: Fix "question reviewed text" not loading in exercise_show</li>
+        <li>[2018-01-11] (<a href="https://github.com/chamilo/chamilo-lms/commit/833cf3b288488020888b824b53fab0003a105328">833cf3b2</a>) Learnpath: Add message when file not found in download_scorm.php</li>
+        <li>[2018-01-11] (<a href="https://github.com/chamilo/chamilo-lms/commit/6b50f0ffaf43c4de8779aeca61b230f327ec5ec6">6b50f0ff</a> - <a href="https://task.beeznest.com/issues/13818">BT#13818</a>) Learnpath: Add response code "404 if file not found by SCORM package</li>
+        <li>[2018-01-11] (<a href="https://github.com/chamilo/chamilo-lms/commit/fd572ef684beaa8c84f7a2e738893952b87e11fb">fd572ef6</a> - <a href="https://task.beeznest.com/issues/13861">BT#13861</a>) Plugin: BBB: Fix BBB when creating a new meeting, remove loop.</li>
+        <li>[2018-01-09] (<a href="https://github.com/chamilo/chamilo-lms/commit/ffe59354c4774707303cff1810f8ee6efd333f21">ffe59354</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2301">GH#2301</a>) Tracking: Fix sort user list in tracking tool</li>
+        <li>[2018-01-09] (<a href="https://github.com/chamilo/chamilo-lms/commit/472c5eaa2dfb686dafbd1e14d14aaad1af529ef3">472c5eaa</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2160">GH#2160</a>) Quiz: Improve code to show test question categories</li>
+        <li>[2018-01-09] (<a href="https://github.com/chamilo/chamilo-lms/commit/ae6494f66062f123764b5cc206a60a06171d578a">ae6494f6</a>) Internal: Fix query not working with symfony/doctrine container (master)</li>
+        <li>[2018-01-09] (<a href="https://github.com/chamilo/chamilo-lms/commit/5eddfed6b8d8fad5257763366feb1f429a639b2a">5eddfed6</a> - <a href="https://task.beeznest.com/issues/13848">BT#13848</a>) Display: Fix course tool order query</li>
+        <li>[2018-01-08] (<a href="https://github.com/chamilo/chamilo-lms/commit/0ae6fa97127d2ff70ae95c92d8ba0cd1212724fc">0ae6fa97</a> - <a href="https://task.beeznest.com/issues/13756">BT#13756</a>) Display: Add function to show the tutors/teachers names through Twig</li>
+        <li>[2018-01-08] (<a href="https://github.com/chamilo/chamilo-lms/commit/20c57607302695dd86fb0fbf1682012d0c47586a">20c57607</a>) Display: Delete footer duplicate</li>
+        <li>[2018-01-08] (<a href="https://github.com/chamilo/chamilo-lms/commit/562085fb4d51af3c70e0ca7545369ddc24bef0c0">562085fb</a> - <a href="https://task.beeznest.com/issues/13602">BT#13602</a>) Announcement: Fix announcement delete action</li>
+        <li>[2018-01-05] (<a href="https://github.com/chamilo/chamilo-lms/commit/fcab01da8ed495e0bede9bb6614db31d5cf56423">fcab01da</a> - <a href="https://task.beeznest.com/issues/13756">BT#13756</a>) Catalogue: Fix date range to search sessions</li>
+        <li>[2018-01-05] (<a href="https://github.com/chamilo/chamilo-lms/commit/3900fd2ada83346c06032a286aea6e98a1a47d1f">3900fd2a</a> - <a href="https://task.beeznest.com/issues/13838">BT#13838</a>) Display: Fix web page after registration</li>
+        <li>[2018-01-05] (<a href="https://github.com/chamilo/chamilo-lms/commit/5d1fc769023bc2edfcf16c99b95353a7ed73f060">5d1fc769</a> - <a href="https://task.beeznest.com/issues/13838">BT#13838</a>) Display: Add Bootstrap style for link inside alert</li>
+        <li>[2018-01-05] (<a href="https://github.com/chamilo/chamilo-lms/commit/f26113ecd83ab3b38a9b33b80911e4d8f482c92c">f26113ec</a> - <a href="https://task.beeznest.com/issues/13838">BT#13838</a>) Catalogue: Show sign-up button only when allow_registration setting is false</li>
+        <li>[2018-01-05] (<a href="https://github.com/chamilo/chamilo-lms/commit/6fa2955a1c5a57f7837f408d0bdac2a6208ad293">6fa2955a</a>) Internal: Add constant WEB_PLUGIN_ASSET_PATH (used in v2)</li>
+        <li>[2018-01-05] (<a href="https://github.com/chamilo/chamilo-lms/commit/3a30ed1c7c7adb290e1a6eb94749ba6e05c31694">3a30ed1c</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2275">GH#2275</a>) Internal: Add use of session namespace to learnpath item view</li>
+        <li>[2018-01-04] (<a href="https://github.com/chamilo/chamilo-lms/commit/c9257e83ec3efeec55703e84dba1bea29c056940">c9257e83</a> - <a href="https://task.beeznest.com/issues/13834">BT#13834</a>) Gradebook: Fix cache paths when exporting to pdf</li>
+        <li>[2018-01-04] (<a href="https://github.com/chamilo/chamilo-lms/commit/95732f801d8e8663052ba9b8de6ed7bd75f88aa0">95732f80</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2275">GH#2275</a>) Learnpath: Fix menu display error</li>
+        <li>[2018-01-03] (<a href="https://github.com/chamilo/chamilo-lms/commit/4f04c164606cd5e09c8b6b6a6300f1573cdb9ba9">4f04c164</a>) Admin : add notice_block to the elements to be sent to the layout tpl</li>
+        <li>[2018-01-03] (<a href="https://github.com/chamilo/chamilo-lms/commit/b12b7e5676c27e17b31ee4122afb8642e644bf45">b12b7e56</a> - <a href="https://task.beeznest.com/issues/13587">BT#13587</a>) Skill: Add "table_of_hierarchical_skill_presentation"</li>
+        <li>[2018-01-02] (<a href="https://github.com/chamilo/chamilo-lms/commit/6f2c2de6349a2801ae33b8143e04ac5a1b25f6f0">6f2c2de6</a>) Ticket: improve icons</li>
+        <li>[2018-01-02] (<a href="https://github.com/chamilo/chamilo-lms/commit/aa55ffb18295092fa1086a7c1cc3dbc1c82debfd">aa55ffb1</a>) Display: Show/hide teacher info depending of setting "display_teacher_in_courselist"</li>
+        <li>[2017-12-30] (<a href="https://github.com/chamilo/chamilo-lms/commit/faf0d7e528a06575d47b7180686b81cee5a79238">faf0d7e5</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2265">GH#2265</a>) Global: Allow audio tag works with htmlpurifier</li>
+        <li>[2017-12-29] (<a href="https://github.com/chamilo/chamilo-lms/commit/6555d8f1f2a595e14b65b30c47cb1de1403a2bce">6555d8f1</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2261">GH#2261</a>) Learnpath: Fix hide left column in LP</li>
+        <li>[2017-12-28] (<a href="https://github.com/chamilo/chamilo-lms/commit/b9e9ac47280576154c1920d0e36a9975ffbc7d8d">b9e9ac47</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2149">GH#2149</a>) Webservice: Rename methods saveNewCourse() and saveNewUser() to addCourse() and addUser() in restApi (missing part)</li>
         <li>[2017-12-28] (<a href="https://github.com/chamilo/chamilo-lms/commit/c94f7cf2f92799d27ca5140d7f7461c2c3ca15b1">c94f7cf2</a> - <a href="https://task.beeznest.com/issues/13822">BT#13822</a>) Learnpath: Fix issue in comparing document path with code path in document source validation before iframe</li>
         <li>[2017-12-28] (<a href="https://github.com/chamilo/chamilo-lms/commit/c7327e9f622ed59ff3fcf6f4bfbe7995851fc5d1">c7327e9f</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2214">GH#2214</a>) Install: Remove minor query blocking migration (intra-version)</li>
         <li>[2017-12-28] (<a href="https://github.com/chamilo/chamilo-lms/commit/d7c55369092023505ac15a0d56b6c54338e53d34">d7c55369</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2149">GH#2149</a>) Webservice: Rename methods saveNewCourse() and saveNewUser() to addCourse() and addUser() in restApi</li>

+ 11 - 1
documentation/optimization.html

@@ -195,7 +195,17 @@ ALTER TABLE c_lp_item_view ADD INDEX idx_clpiv_c_i_v (c_id, id, view_count);
     find out about these possible bottlenecks after we release stable versions of Chamilo. This is why we list those
     queries here. However, as soon as we confirm them with a few real life scenarios, we add them into the core of
     Chamilo so you can benefit from them immediately by installing a new version.
-
+    <p>In Chamilo 1.11.x you can boost the DB tables related surveys invitations by adding the following indexes:</p>
+    <pre>
+        CREATE INDEX idx_survey_q_qid ON c_survey_question (question_id);
+        CREATE INDEX idx_survey_code ON c_survey (code);
+        CREATE INDEX idx_survey_inv_code ON c_survey_invitation (survey_code);
+        CREATE INDEX idx_survey_qo_qid ON c_survey_question_option (question_id);
+    </pre>
+    Also by adding a index on access_url_rel_session to improve the course/session list
+    <pre>
+        CREATE INDEX idx_accessurs_sid ON access_url_rel_session (session_id);
+    </pre>
 <hr />
 <h2><a name="3.Indexes-caching"></a>3. Indexes caching</h2>
 One good reference: <a href="http://dev.mysql.com/doc/refman/5.6/en/multiple-key-caches.html">MySQL documentation on multiple key caches</a><br />

+ 2 - 1
documentation/security.html

@@ -10,6 +10,7 @@
 <body>
 <div class="container">
 <h1>Chamilo LMS: Security Guide</h1>
+
 <a href="index.html">Documentation</a> &gt; Security Guide
 
 <p>We recommend you don't take security issues too lightly. Chamilo is security-audited at least once a year,
@@ -85,7 +86,7 @@ This will prevent direct access to your settings and make it seem totally the sa
     <p>Don't hesitate to hire an experienced administrator to do that,
         it might be a bit more expensive now, but you'll be happy not to have to loose
         all of your data to a hacker who attacked your site.</p>
-    <p>Only the following directories have required (or optional) write
+    <p>Only the following directories have required (or optional) write 
         permissions from the web server:<br />
         <ul>
           <li>app/cache/</li>

+ 0 - 1
main/admin/skill.php

@@ -64,7 +64,6 @@ switch ($action) {
 
         if ($form->validate()) {
             $values = $form->exportValues();
-
             $profile = $em->getRepository('ChamiloSkillBundle:Profile')->find($values['profile_id']);
             if ($profile) {
                 $item->setProfile($profile);

+ 3 - 5
main/admin/skill_badge_create.php

@@ -89,11 +89,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
     exit;
 }
 
-$interbreadcrumb = [
-    [
-        'url' => api_get_path(WEB_CODE_PATH).'admin/index.php',
-        'name' => get_lang('Administration'),
-    ],
+$interbreadcrumb[] = [
+    'url' => api_get_path(WEB_CODE_PATH).'admin/index.php',
+    'name' => get_lang('Administration'),
 ];
 $interbreadcrumb[] = ['url' => 'skill_list.php', 'name' => get_lang('ManageSkills')];
 

+ 86 - 0
main/admin/skill_rel_course.php

@@ -0,0 +1,86 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+use Chamilo\SkillBundle\Entity\SkillRelCourse;
+
+$cidReset = true;
+require_once __DIR__.'/../inc/global.inc.php';
+
+if (api_get_configuration_value('allow_skill_rel_items') == false) {
+    api_not_allowed(true);
+}
+
+$courseId = isset($_GET['course_id']) ? (int) $_GET['course_id'] : 0;
+
+$course = api_get_course_entity($courseId);
+if (empty($course)) {
+    api_not_allowed(true);
+}
+
+$sessionId = isset($_GET['session_id']) ? (int) $_GET['session_id'] : null;
+
+$url = api_get_self().'?course_id='.$courseId.'&session_id='.$sessionId;
+$form = new FormValidator('skills', 'post', $url);
+
+$sessionName = $course->getTitleAndCode();
+if (!empty($sessionId)) {
+    $session = api_get_session_entity($sessionId);
+    $courseExistsInSession = SessionManager::sessionHasCourse($sessionId, $course->getCode());
+    if (!$courseExistsInSession) {
+        api_not_allowed(true);
+    }
+    $sessionName = ' '.$session->getName().' - '.$course->getTitleAndCode();
+}
+
+$form->addHeader(get_lang('AddSkills').$sessionName);
+
+$skillList = [];
+$em = Database::getManager();
+$items = $em->getRepository('ChamiloSkillBundle:SkillRelCourse')->findBy(
+    ['course' => $courseId, 'session' => $sessionId]
+);
+/** @var SkillRelCourse $skillRelCourse */
+foreach ($items as $skillRelCourse) {
+    $skillList[$skillRelCourse->getSkill()->getId()] = $skillRelCourse->getSkill()->getName();
+}
+
+$form->addHidden('course_id', $courseId);
+$form->addHidden('session_id', $sessionId);
+
+$form->addSelectAjax(
+    'skills',
+    get_lang('Skills'),
+    $skillList,
+    [
+        'url' => api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=search_skills',
+        'multiple' => 'multiple',
+    ]
+);
+
+$form->addButtonSave(get_lang('Save'));
+
+$form->setDefaults(['skills' => array_keys($skillList)]);
+
+if ($form->validate()) {
+    $result = Skill::saveSkillsToCourseFromForm($form);
+    if ($result) {
+        Display::addFlash(Display::return_message(get_lang('Updated')));
+    }
+    header('Location: '.$url);
+    exit;
+}
+$content = $form->returnForm();
+
+$interbreadcrumb[] = [
+    'url' => api_get_path(WEB_CODE_PATH).'session/session_list.php',
+    'name' => get_lang('SessionList'),
+];
+
+$interbreadcrumb[] = [
+    'url' => api_get_path(WEB_CODE_PATH).'session/resume_session.php?id_session='.$sessionId,
+    'name' => get_lang('SessionOverview'),
+];
+
+$template = new Template(get_lang('SkillRelCourses'));
+$template->assign('content', $content);
+$template->display_one_col_template();

+ 7 - 7
main/course_home/course_home.php

@@ -331,6 +331,13 @@ if ($allow === true) {
 
 $content = '<div id="course_tools">'.$diagram.$content.'</div>';
 
+// Deleting the objects
+Session::erase('_gid');
+Session::erase('oLP');
+Session::erase('lpobject');
+api_remove_in_gradebook();
+DocumentManager::removeGeneratedAudioTempFile();
+
 $tpl = new Template(null);
 $tpl->assign('message', $show_message);
 $tpl->assign('content', $content);
@@ -338,10 +345,3 @@ $tpl->assign('content', $content);
 // Direct login to course
 $tpl->assign('course_code', $course_code);
 $tpl->display_one_col_template();
-
-// Deleting the objects
-Session::erase('_gid');
-Session::erase('oLP');
-Session::erase('lpobject');
-api_remove_in_gradebook();
-DocumentManager::removeGeneratedAudioTempFile();

+ 7 - 0
main/course_info/infocours.php

@@ -504,6 +504,13 @@ if ($allowLPReturnLink === 'true') {
             get_lang('RedirectToCourseHome'),
             0
         ),
+        $form->createElement(
+            'radio',
+            'lp_return_link',
+            null,
+            get_lang('MyCourses'),
+            2
+        ),
     ];
     $form->addGroup($group, '', [get_lang("LpReturnLink")]);
 }

+ 0 - 1
main/course_progress/thematic_controller.php

@@ -460,7 +460,6 @@ class ThematicController
                     $data['default_thematic_plan_icon'] = $thematic->get_default_thematic_plan_icon();
                     $data['default_thematic_plan_question'] = $thematic->get_default_question();
                     $data['next_description_type'] = $thematic->get_next_description_type($_POST['thematic_id']);
-
                     // render to the view
                     $this->view->set_data($data);
                     $this->view->set_layout('layout');

+ 0 - 4
main/document/document.php

@@ -2097,10 +2097,6 @@ if (count($documentAndFolders) > 1) {
         $form_action['set_invisible'] = get_lang('SetInvisible');
         $form_action['set_visible'] = get_lang('SetVisible');
         $form_action['delete'] = get_lang('Delete');
-        /*$portfolio_actions = Portfolio::actions();
-        foreach ($portfolio_actions as $action) {
-            $form_action[$action->get_name()] = $action->get_title();
-        }*/
         $table->set_form_actions($form_action, 'ids');
     }
 }

+ 16 - 21
main/exercise/exercise.class.php

@@ -5869,19 +5869,22 @@ class Exercise
     }
 
     /**
-     * @param array  $user_data  result of api_get_user_info()
-     * @param string $start_date
-     * @param null   $duration
-     * @param string $ip         Optional. The user IP
+     * @param array $user_data         result of api_get_user_info()
+     * @param array $trackExerciseInfo result of get_stat_track_exercise_info
      *
      * @return string
      */
-    public function show_exercise_result_header(
+    public function showExerciseResultHeader(
         $user_data,
-        $start_date = null,
-        $duration = null,
-        $ip = null
+        $trackExerciseInfo
     ) {
+        $start_date = null;
+        if (isset($trackExerciseInfo['start_date'])) {
+            $start_date = api_convert_and_format_date($trackExerciseInfo['start_date']);
+        }
+        $duration = isset($trackExerciseInfo['duration_formatted']) ? $trackExerciseInfo['duration_formatted'] : null;
+        $ip = isset($trackExerciseInfo['user_ip']) ? $trackExerciseInfo['user_ip'] : null;
+
         $array = [];
         if (!empty($user_data)) {
             $array[] = [
@@ -5958,6 +5961,7 @@ class Exercise
      * @param int     Whether the results are show to the user (0) or not (1)
      * @param int     Maximum number of attempts (0 if no limit)
      * @param int     Feedback type
+     * @param int $propagateNegative
      *
      * @todo this was function was added due the import exercise via CSV
      *
@@ -6630,21 +6634,12 @@ class Exercise
         $new_array = [];
         if (Database::num_rows($result) > 0) {
             $new_array = Database::fetch_array($result, 'ASSOC');
-            $new_array['duration'] = null;
             $start_date = api_get_utc_datetime($new_array['start_date'], true);
             $end_date = api_get_utc_datetime($new_array['exe_date'], true);
-
-            if (!empty($start_date) && !empty($end_date)) {
-                $start_date = api_strtotime($start_date, 'UTC');
-                $end_date = api_strtotime($end_date, 'UTC');
-                if ($start_date && $end_date) {
-                    $mytime = $end_date - $start_date;
-                    $new_learnpath_item = new learnpathItem(null);
-                    $time_attemp = $new_learnpath_item->get_scorm_time('js', $mytime);
-                    $h = get_lang('h');
-                    $time_attemp = str_replace('NaN', '00'.$h.'00\'00"', $time_attemp);
-                    $new_array['duration'] = $time_attemp;
-                }
+            $new_array['duration_formatted'] = '';
+            if (!empty($new_array['exe_duration']) && !empty($start_date) && !empty($end_date)) {
+                $time = api_format_time($new_array['exe_duration'], 'js');
+                $new_array['duration_formatted'] = $time;
             }
         }
 

+ 2 - 6
main/exercise/exercise_show.php

@@ -35,7 +35,6 @@ if (empty($track_exercise_info)) {
 }
 
 $exercise_id = $track_exercise_info['id'];
-$exercise_date = $track_exercise_info['start_date'];
 $student_id = $track_exercise_info['exe_user_id'];
 $learnpath_id = $track_exercise_info['orig_lp_id'];
 $learnpath_item_id = $track_exercise_info['orig_lp_item_id'];
@@ -123,7 +122,6 @@ if (empty($objExercise)) {
     $objExercise->read($exercise_id);
 }
 $feedback_type = $objExercise->feedback_type;
-
 //Only users can see their own results
 if (!$is_allowedToEdit) {
     if ($student_id != $currentUserId) {
@@ -293,11 +291,9 @@ if ($action == 'export') {
 $user_info = api_get_user_info($student_id);
 if ($show_results || $show_only_total_score || $showTotalScoreAndUserChoicesInLastAttempt) {
     // Shows exercise header
-    echo $objExercise->show_exercise_result_header(
+    echo $objExercise->showExerciseResultHeader(
         $user_info,
-        api_convert_and_format_date($exercise_date),
-        null,
-        $track_exercise_info['user_ip']
+        $track_exercise_info
     );
 }
 

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

@@ -18,9 +18,8 @@ class HotpotatoesExerciseResult
     /**
      * Gets the results of all students (or just one student if access is limited).
      *
-     * @param    string        The document path (for HotPotatoes retrieval)
+     * @param string $document_path The document path (for HotPotatoes retrieval)
      * @param    int        User ID. Optional. If no user ID is provided, we take all the results. Defauts to null
-     * @param string $document_path
      *
      * @return bool
      */
@@ -29,10 +28,7 @@ class HotpotatoesExerciseResult
         $return = [];
         $TBL_USER = Database::get_main_table(TABLE_MAIN_USER);
         $TBL_TRACK_HOTPOTATOES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);
-
-        $cid = api_get_course_id();
         $course_id = api_get_course_int_id();
-        //$user_id         = intval($user_id);
         $user_id = null;
         $session_id_and = ' AND te.session_id = '.api_get_session_id().' ';
         $hotpotato_name = Database::escape_string($hotpotato_name);

+ 0 - 6
main/exercise/upload_exercise.php

@@ -546,13 +546,7 @@ function lp_upload_quiz_action_handling()
         if (!empty($lpObject)) {
             $oLP = unserialize($lpObject);
             if (is_object($oLP)) {
-                if ($debug > 0) {
-                    error_log('New LP - oLP is object', 0);
-                }
                 if ((empty($oLP->cc)) || $oLP->cc != api_get_course_id()) {
-                    if ($debug > 0) {
-                        error_log('New LP - Course has changed, discard lp object', 0);
-                    }
                     $oLP = null;
                     Session::erase('oLP');
                     Session::erase('lpobject');

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

@@ -698,7 +698,6 @@ function store_forum($values, $courseInfo = [], $returnId = false)
     }
 
     // Forum images
-    $image_moved = false;
     $has_attachment = false;
     $image_moved = true;
     if (!empty($_FILES['picture']['name'])) {
@@ -5575,30 +5574,6 @@ function count_number_of_post_for_user_thread($thread_id, $user_id)
     return $count;
 }
 
-/**
- * This function counts the number of user register in course.
- *
- * @param int $course_id Course ID
- *
- * @deprecated use CourseManager::get_users_count_in_course
- *
- * @return int the number of user register in course
- *
- * @author Jhon Hinojosa <jhon.hinojosa@dokeos.com>,
- *
- * @version octubre 2008, dokeos 1.8
- */
-function count_number_of_user_in_course($course_id)
-{
-    $table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
-
-    $sql = "SELECT * FROM $table
-            WHERE c_id ='".intval($course_id)."' ";
-    $result = Database::query($sql);
-
-    return count(Database::store_result($result));
-}
-
 /**
  * This function retrieves information of statistical.
  *

+ 98 - 29
main/glossary/index.php

@@ -25,8 +25,33 @@ $htmlHeadXtra[] = '<script>
 function setFocus(){
     $("#glossary_title").focus();
 }
+
 $(document).ready(function () {
     setFocus();
+    $( "#dialog:ui-dialog" ).dialog( "destroy" );
+    $( "#dialog-confirm" ).dialog({
+        autoOpen: false,
+        show: "blind",
+        resizable: false,
+        height:300,
+        modal: true
+    });
+    $("#export_opener").click(function() {
+        var targetUrl = $(this).attr("href");        
+        $( "#dialog-confirm" ).dialog({
+            width:400,
+            height:300,
+            buttons: {
+                "'.addslashes(get_lang('Download')).'": function() {
+                    var export_format = $("input[name=export_format]:checked").val();
+                    location.href = targetUrl+"&export_format="+export_format;
+                    $( this ).dialog( "close" );
+                }
+            }
+        });
+        $( "#dialog-confirm" ).dialog("open");
+        return false;
+    });
 });
 </script>';
 
@@ -225,11 +250,28 @@ switch ($action) {
             'post',
             api_get_self().'?action=import&'.api_get_cidreq()
         );
-        $form->addHeader('header', get_lang('ImportGlossary'));
-        $form->addElement('file', 'file', get_lang('ImportCSVFileLocation'));
+        $form->addHeader(get_lang('ImportGlossary'));
+        $form->addElement('file', 'file', get_lang('File'));
+        $group = [];
+        $group[] = $form->createElement(
+            'radio',
+            'file_type',
+            '',
+            'CSV',
+            'csv'
+        );
+        $group[] = $form->createElement(
+            'radio',
+            'file_type',
+            '',
+            'XLS',
+            'xls'
+        );
+        $form->addGroup($group, '', get_lang('FileType'), null);
         $form->addElement('checkbox', 'replace', null, get_lang('DeleteAllGlossaryTerms'));
         $form->addElement('checkbox', 'update', null, get_lang('UpdateExistingGlossaryTerms'));
         $form->addButtonImport(get_lang('Import'), 'SubmitImport');
+        $form->setDefaults(['file_type' => 'csv']);
         $content = $form->returnForm();
 
         $content .= get_lang('CSVMustLookLike').' ('.get_lang('MandatoryFields').')';
@@ -240,6 +282,8 @@ switch ($action) {
         </pre>';
 
         if ($form->validate()) {
+            $values = $form->getSubmitValues();
+
             $termsDeleted = [];
             //this is a bad idea //jm
             if (isset($_POST['replace']) && $_POST['replace']) {
@@ -255,7 +299,17 @@ switch ($action) {
             }
 
             $updateTerms = isset($_POST['update']) && $_POST['update'] ? true : false;
-            $data = Import::csvToArray($_FILES['file']['tmp_name']);
+
+            $format = $values['file_type'];
+            switch ($format) {
+                case 'csv':
+                    $data = Import::csvToArray($_FILES['file']['tmp_name']);
+                    break;
+                case 'xls':
+                    $data = Import::xlsToArray($_FILES['file']['tmp_name']);
+                    break;
+            }
+
             $goodList = [];
             $updatedList = [];
             $addedList = [];
@@ -368,32 +422,8 @@ switch ($action) {
         if (!api_is_allowed_to_edit(null, true)) {
             api_not_allowed(true);
         }
-        $data = GlossaryManager::get_glossary_data(
-            0,
-            GlossaryManager::get_number_glossary_terms(api_get_session_id()),
-            0,
-            'ASC'
-        );
-
-        usort($data, 'sorter');
-        $list = [];
-        $list[] = ['term', 'definition'];
-        $allowStrip = api_get_configuration_value('allow_remove_tags_in_glossary_export');
-        foreach ($data as $line) {
-            $definition = $line[1];
-            if ($allowStrip) {
-                $definition = strip_tags($definition);
-            }
-            $list[] = [$line[0], $definition];
-        }
-        $filename = 'glossary_course_'.api_get_course_id();
-        Export::arrayToCsv($list, $filename);
-        break;
-    case 'export_to_pdf':
-        if (!api_is_allowed_to_edit(null, true)) {
-            api_not_allowed(true);
-        }
-        GlossaryManager::export_to_pdf();
+        $format = isset($_GET['export_format']) ? $_GET['export_format'] : 'csv';
+        GlossaryManager::exportToFormat($format);
         break;
     case 'changeview':
         if (in_array($_GET['view'], ['list', 'table'])) {
@@ -429,4 +459,43 @@ Display::display_introduction_section(TOOL_GLOSSARY);
 
 echo $content;
 
+$extra = '<div id="dialog-confirm" title="'.get_lang("ConfirmYourChoice").'">';
+$form = new FormValidator(
+    'report',
+    'post',
+    api_get_self().'?'.api_get_cidreq(),
+    null,
+    ['class' => 'form-vertical']
+);
+$form->addElement(
+    'radio',
+    'export_format',
+    null,
+    get_lang('ExportAsCSV'),
+    'csv',
+    ['id' => 'export_format_csv_label']
+);
+$form->addElement(
+    'radio',
+    'export_format',
+    null,
+    get_lang('ExportAsXLS'),
+    'xls',
+    ['id' => 'export_format_xls_label']
+);
+$form->addElement(
+    'radio',
+    'export_format',
+    null,
+    get_lang('ExportToPDF'),
+    'pdf',
+    ['id' => 'export_format_pdf_label']
+);
+
+$form->setDefaults(['export_format' => 'csv']);
+$extra .= $form->returnForm();
+$extra .= '</div>';
+
+echo $extra;
+
 Display::display_footer();

+ 1 - 1
main/gradebook/gradebook_display_summary.php

@@ -29,7 +29,7 @@ $userList = CourseManager::get_user_list_from_course_code(
     $sessionId,
     null,
     null,
-    $statusFilter
+    $statusToFilter
 );
 
 switch ($action) {

+ 32 - 11
main/gradebook/index.php

@@ -666,7 +666,6 @@ if (isset($_GET['studentoverview'])) {
     //if $category = 0 (which happens when GET['selectcat'] is undefined)
     // then Category::load() will create a new 'root' category with empty
     // course and session fields in memory (Category::create_root_category())
-
     if ($_in_course === true) {
         // When *inside* a course, we want to make sure there is one (and only
         // one) category for this course or for this session.
@@ -692,8 +691,8 @@ if (isset($_GET['studentoverview'])) {
             // There is no category for this course+session, so create one
             $cat = new Category();
             if (!empty($session_id)) {
-                $s_name = api_get_session_name($session_id);
-                $cat->set_name($course_code.' - '.get_lang('Session').' '.$s_name);
+                $sessionName = api_get_session_name($session_id);
+                $cat->set_name($course_code.' - '.get_lang('Session').' '.$sessionName);
                 $cat->set_session_id($session_id);
             } else {
                 $cat->set_name($course_code);
@@ -895,6 +894,7 @@ if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)
 
         $i = 0;
         $allcat = [];
+
         /** @var Category $cat */
         foreach ($cats as $cat) {
             $allcat = $cat->get_subcategories($stud_id, $course_code, $session_id);
@@ -934,13 +934,24 @@ if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)
                     $exportToPdf = true;
                 }
 
+                $teacher = api_is_allowed_to_edit(null, true);
+                if ($teacher) {
+                    $loadStats = false;
+                } else {
+                    $loadStats = api_get_configuration_value('disable_gradebook_stats') === false;
+                }
+
                 $gradebookTable = new GradebookTable(
                     $cat,
                     $allcat,
                     $alleval,
                     $alllink,
                     $addparams,
-                    $exportToPdf
+                    $exportToPdf,
+                    null,
+                    null,
+                    [],
+                    $loadStats
                 );
 
                 $model = ExerciseLib::getCourseScoreModel();
@@ -951,13 +962,23 @@ if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)
                     ];
                 } else {
                     if (empty($model)) {
-                        $gradebookTable->td_attributes = [
-                            3 => 'class="text-right"',
-                            4 => 'class="text-center"',
-                            5 => 'class="text-center"',
-                            6 => 'class="text-center"',
-                            7 => 'class="text-center"',
-                        ];
+                        if ($loadStats) {
+                            $gradebookTable->td_attributes = [
+                                3 => 'class="text-right"',
+                                4 => 'class="text-center"',
+                                5 => 'class="text-center"',
+                                6 => 'class="text-center"',
+                                7 => 'class="text-center"',
+                            ];
+                        } else {
+                            $gradebookTable->td_attributes = [
+                                3 => 'class="text-right"',
+                                4 => 'class="text-center"',
+                                5 => 'class="text-center"',
+                                //6 => 'class="text-center"',
+                                //7 => 'class="text-center"',
+                            ];
+                        }
                     } else {
                         $gradebookTable->td_attributes = [
                             3 => 'class="text-right"',

+ 4 - 0
main/gradebook/lib/be/abstractlink.class.php

@@ -214,6 +214,10 @@ abstract class AbstractLink implements GradebookItem
 
     public function getStudentList()
     {
+        if (empty($this->studentList)) {
+            return [];
+        }
+
         return $this->studentList;
     }
 

+ 3 - 14
main/gradebook/lib/be/category.class.php

@@ -836,9 +836,9 @@ class Category implements GradebookItem
             $name = $this->name;
             $parent = $this->parent;
         }
-        $tbl_grade_categories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
+        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
         $sql = "SELECT count(id) AS number
-                FROM $tbl_grade_categories
+                FROM $table
                 WHERE name = '".Database::escape_string($name)."'";
 
         if (api_is_allowed_to_edit()) {
@@ -864,7 +864,6 @@ class Category implements GradebookItem
         } else {
             $sql .= ' AND parent_id = '.intval($parent);
         }
-
         $result = Database::query($sql);
         $number = Database::fetch_row($result);
 
@@ -1509,6 +1508,7 @@ class Category implements GradebookItem
                 WHERE 
                     cc.id = cu.c_id AND 
                     cu.status = '.COURSEMANAGER;
+
         if (!api_is_platform_admin()) {
             $sql .= ' AND cu.user_id = '.$user_id;
         }
@@ -1690,17 +1690,6 @@ class Category implements GradebookItem
         $session_id = null,
         $order = null
     ) {
-        if (!empty($session_id)) {
-            /*$tbl_grade_categories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
-            $sql = 'SELECT id FROM '.$tbl_grade_categories. ' WHERE session_id = '.$session_id;
-            $result_session = Database::query($sql);
-            if (Database::num_rows($result_session) > 0) {
-                $data_session = Database::fetch_array($result_session);
-                $parent_id = $data_session['id'];
-                return self::load(null, null, null, $parent_id, null, null, $order);
-            }*/
-        }
-
         // 1 student
         if (isset($studentId)) {
             // Special case: this is the root

+ 60 - 32
main/gradebook/lib/fe/gradebooktable.class.php

@@ -25,6 +25,7 @@ class GradebookTable extends SortableTable
     private $datagen;
     private $evals_links;
     private $dataForGraph;
+    private $loadStats = true;
 
     /**
      * GradebookTable constructor.
@@ -48,7 +49,8 @@ class GradebookTable extends SortableTable
         $exportToPdf = false,
         $showTeacherView = null,
         $userId = null,
-        $studentList = []
+        $studentList = [],
+        $loadStats = true
     ) {
         $this->teacherView = is_null($showTeacherView) ? api_is_allowed_to_edit(null, true) : $showTeacherView;
         $this->userId = is_null($userId) ? api_get_user_id() : $userId;
@@ -67,6 +69,7 @@ class GradebookTable extends SortableTable
         $this->evals_links = array_merge($evals, $links);
         $this->currentcat = $currentcat;
         $this->cats = $cats;
+        $this->loadStats = $loadStats;
         $this->datagen = new GradebookDataGenerator($cats, $evals, $links);
 
         if (!empty($userId)) {
@@ -104,9 +107,13 @@ class GradebookTable extends SortableTable
             $this->set_header($column++, get_lang('Weight'), false);
             $this->set_header($column++, get_lang('Result'), false);
             if (empty($model)) {
-                $this->set_header($column++, get_lang('Ranking'), false);
+                if ($this->loadStats) {
+                    $this->set_header($column++, get_lang('Ranking'), false);
+                }
                 $this->set_header($column++, get_lang('BestScore'), false);
-                $this->set_header($column++, get_lang('Average'), false);
+                if ($this->loadStats) {
+                    $this->set_header($column++, get_lang('Average'), false);
+                }
             }
 
             if (!empty($cats)) {
@@ -214,13 +221,12 @@ class GradebookTable extends SortableTable
         $course_code = api_get_course_id();
         $session_id = api_get_session_id();
 
+        $statusToFilter = 0;
         if (empty($session_id)) {
             $statusToFilter = STUDENT;
-        } else {
-            $statusToFilter = 0;
         }
 
-        if (empty($this->studentList)) {
+        if (empty($this->studentList) && $this->loadStats) {
             $studentList = CourseManager::get_user_list_from_course_code(
                 $course_code,
                 $session_id,
@@ -237,7 +243,8 @@ class GradebookTable extends SortableTable
             $from,
             $this->per_page,
             false,
-            $this->studentList
+            $this->studentList,
+            $this->loadStats
         );
 
         // generate the data to display
@@ -348,14 +355,13 @@ class GradebookTable extends SortableTable
                     }
                 } else {
                     $score = $item->calc_score($this->userId);
-
+                    $scoreToDisplay = '-';
                     if (!empty($score[1])) {
                         $completeScore = $scoredisplay->display_score($score, SCORE_DIV_PERCENT);
                         $score = $score[0] / $score[1] * $item->get_weight();
                         $score = $scoredisplay->display_score([$score, null], SCORE_SIMPLE);
                         $scoreToDisplay = Display::tip($score, $completeScore);
                     } else {
-                        $scoreToDisplay = '-';
                         $categoryScore = null;
                     }
 
@@ -407,11 +413,17 @@ class GradebookTable extends SortableTable
 
                         if (empty($model)) {
                             // Ranking
-                            $row[] = $ranking;
+                            if ($this->loadStats) {
+                                $row[] = $ranking;
+                            }
+
                             // Best
                             $row[] = $best;
+
                             // Average
-                            $row[] = $average;
+                            if ($this->loadStats) {
+                                $row[] = $average;
+                            }
                         }
 
                         if (get_class($item) == 'Category') {
@@ -454,7 +466,6 @@ class GradebookTable extends SortableTable
 
                         $sub_cat_info = new GradebookDataGenerator($allcat, $alleval, $alllink);
                         $sub_cat_info->userId = $user_id;
-
                         $data_array2 = $sub_cat_info->get_data(
                             $sorting,
                             $from,
@@ -488,7 +499,7 @@ class GradebookTable extends SortableTable
                             $row[] = $this->build_type_column($item, ['style' => 'padding-left:5px']);
 
                             // Name.
-                            $row[] = $invisibility_span_open."&nbsp;&nbsp;&nbsp;  ".
+                            $row[] = $invisibility_span_open."&nbsp;&nbsp;&nbsp; ".
                                 $this->build_name_link($item, $type).$invisibility_span_close;
 
                             // Description.
@@ -528,22 +539,24 @@ class GradebookTable extends SortableTable
                                 if (count($eval_n_links) > 0) {
                                     $value_data = isset($data[4]) ? $data[4] : null;
                                     if (!is_null($value_data)) {
-                                        //$score = $item->calc_score(api_get_user_id());
-                                        //$new_score = $data[3] * $score[0] / $score[1];
-                                        //$new_score = floatval(number_format($new_score, api_get_setting('gradebook_number_decimals')));
-
                                         // Result
                                         $row[] = $value_data;
                                         $best = isset($data['best']) ? $data['best'] : null;
                                         $average = isset($data['average']) ? $data['average'] : null;
                                         $ranking = isset($data['ranking']) ? $data['ranking'] : null;
 
-                                        // Ranking
-                                        $row[] = $ranking;
+                                        if ($this->loadStats) {
+                                            // Ranking
+                                            $row[] = $ranking;
+                                        }
+
                                         // Best
                                         $row[] = $best;
+
                                         // Average
-                                        $row[] = $average;
+                                        if ($this->loadStats) {
+                                            $row[] = $average;
+                                        }
                                     }
                                 }
 
@@ -709,16 +722,29 @@ class GradebookTable extends SortableTable
                         $totalAverage,
                     ];
                 } else {
-                    $row = [
-                        null,
-                        '<h3>'.get_lang('Total').'</h3>',
-                        null,
-                        $main_weight,
-                        $totalResult,
-                        $totalRanking,
-                        $totalBest,
-                        $totalAverage,
-                    ];
+                    if ($this->loadStats) {
+                        $row = [
+                            null,
+                            '<h3>'.get_lang('Total').'</h3>',
+                            null,
+                            $main_weight,
+                            $totalResult,
+                            $totalRanking,
+                            $totalBest,
+                            $totalAverage,
+                        ];
+                    } else {
+                        $row = [
+                            null,
+                            '<h3>'.get_lang('Total').'</h3>',
+                            null,
+                            $main_weight,
+                            $totalResult,
+                            //$totalRanking,
+                            $totalBest,
+                            //$totalAverage,
+                        ];
+                    }
                 }
 
                 $sortable_data[] = $row;
@@ -824,8 +850,10 @@ class GradebookTable extends SortableTable
                 SCORE_DIV_PERCENT_WITH_CUSTOM
             );
             $rowTotal[] = ' ';
-            $rowTotal[] = ' ';
-            $rowTotal[] = ' ';
+            if ($this->loadStats) {
+                $rowTotal[] = ' ';
+                $rowTotal[] = ' ';
+            }
 
             $sortable_data[] = $rowTotal;
         }

+ 19 - 14
main/gradebook/lib/gradebook_data_generator.class.php

@@ -90,7 +90,8 @@ class GradebookDataGenerator
         $start = 0,
         $count = null,
         $ignore_score_color = false,
-        $studentList = []
+        $studentList = [],
+        $loadStats = true
     ) {
         // do some checks on count, redefine if invalid value
         if (!isset($count)) {
@@ -107,7 +108,7 @@ class GradebookDataGenerator
 
         // Get selected items
         $visibleItems = array_slice($allitems, $start, $count);
-        $userCount = count($studentList);
+        $userCount = !empty($studentList) ? count($studentList) : 0;
 
         // Generate the data to display
         $data = [];
@@ -154,6 +155,7 @@ class GradebookDataGenerator
                     $ranking = $this->buildRankingColumn($item, $userId, $userCount);
                     $row['ranking'] = $ranking['display'];
                     $row['ranking_score'] = $ranking['score'];
+
                     $row[] = $item;
                 }
             } else {
@@ -173,26 +175,29 @@ class GradebookDataGenerator
                 $row['best'] = $best['display'];
                 $row['best_score'] = $best['score'];
 
+                $rankingStudentList = [];
+                $invalidateResults = true;
+
                 // Average
                 $average = $this->buildAverageResultColumn($item);
                 $row['average'] = $average['display'];
                 $row['average_score'] = $average['score'];
 
                 // Ranking
-                $rankingStudentList = [];
-                $invalidateResults = true;
-                foreach ($studentList as $user) {
-                    $score = $this->build_result_column(
-                        $user['user_id'],
-                        $item,
-                        $ignore_score_color,
-                        true
-                    );
+                if (!empty($studentList)) {
+                    foreach ($studentList as $user) {
+                        $score = $this->build_result_column(
+                            $user['user_id'],
+                            $item,
+                            $ignore_score_color,
+                            true
+                        );
 
-                    if (!empty($score['score'][0])) {
-                        $invalidateResults = false;
+                        if (!empty($score['score'][0])) {
+                            $invalidateResults = false;
+                        }
+                        $rankingStudentList[$user['user_id']] = $score['score'][0];
                     }
-                    $rankingStudentList[$user['user_id']] = $score['score'][0];
                 }
 
                 $scoreDisplay = ScoreDisplay::instance();

+ 6 - 5
main/gradebook/lib/gradebook_result.class.php

@@ -81,7 +81,8 @@ class GradeBookResult
      *
      * @param array $data
      *
-     * @return bool|null False on error
+     * @throws PHPExcel_Exception
+     * @throws PHPExcel_Writer_Exception
      */
     public function exportCompleteReportXLS($data)
     {
@@ -91,14 +92,14 @@ class GradeBookResult
         $spreadsheet->setActiveSheetIndex(0);
         $worksheet = $spreadsheet->getActiveSheet();
 
-        $line = 0;
-        $column = 1;
+        $line = 1;
+        $column = 0;
 
         //headers
         foreach ($data[0] as $header_col) {
             $worksheet->SetCellValueByColumnAndRow(
-                $line,
                 $column,
+                $line,
                 html_entity_decode(strip_tags($header_col))
             );
             $column++;
@@ -111,8 +112,8 @@ class GradeBookResult
             $column = 0;
             foreach ($data[1][$i] as $col_name) {
                 $worksheet->SetCellValueByColumnAndRow(
-                    $line,
                     $column,
+                    $line,
                     html_entity_decode(strip_tags($col_name))
                 );
                 $column++;

+ 87 - 0
main/gradebook/skill_rel_user.php

@@ -0,0 +1,87 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+use Chamilo\SkillBundle\Entity\SkillRelItem;
+
+require_once __DIR__.'/../inc/global.inc.php';
+
+if (api_get_configuration_value('allow_skill_rel_items') == false) {
+    api_not_allowed(true);
+}
+
+api_protect_course_script();
+GradebookUtils::block_students();
+$courseId = api_get_course_int_id();
+$sessionId = api_get_session_id();
+
+$userId = isset($_GET['user_id']) ? (int) $_GET['user_id'] : 0;
+$categoryId = isset($_GET['selectcat']) ? (int) $_GET['selectcat'] : 0;
+$userInfo = api_get_user_info($userId);
+
+if (empty($userInfo)) {
+    api_not_allowed(true);
+}
+
+$skills = Skill::getSkillRelItemsPerCourse($courseId, $sessionId);
+$uniqueSkills = [];
+$itemsPerSkill = [];
+$uniqueSkillsConclusion = [];
+$skillRelUser = new SkillRelUser();
+$userSkills = $skillRelUser->getUserSkills($userId, api_get_course_int_id(), api_get_session_id());
+$userSkillsList = [];
+if (!empty($userSkills)) {
+    foreach ($userSkills as $userSkill) {
+        $userSkillsList[] = $userSkill['skill_id'];
+    }
+}
+
+$em = Database::getManager();
+$codePath = api_get_path(WEB_CODE_PATH);
+/** @var SkillRelItem $skill */
+foreach ($skills as $skill) {
+    $skillId = $skill->getSkill()->getId();
+    $uniqueSkills[$skillId] = $skill->getSkill();
+    $itemInfo = Skill::getItemInfo($skill->getItemId(), $skill->getItemType());
+
+    $criteria = [
+        'user' => $userId,
+        'skillRelItem' => $skill,
+    ];
+    /** @var \Chamilo\SkillBundle\Entity\SkillRelItemRelUser $skillRelItemRelUser */
+    $skillRelItemRelUser = $em->getRepository('ChamiloSkillBundle:SkillRelItemRelUser')->findOneBy($criteria);
+    $itemInfo['status'] = $skillRelItemRelUser ? true : false;
+    $itemInfo['url_activity'] = $codePath.$skill->getItemResultList(api_get_cidreq());
+    if ($skillRelItemRelUser) {
+        $itemInfo['url_activity'] = $codePath.$skillRelItemRelUser->getUserItemResultUrl(api_get_cidreq());
+    }
+
+    $itemsPerSkill[$skillId][]['info'] = $itemInfo;
+}
+foreach ($itemsPerSkill as $skillId => $skillList) {
+    $uniqueSkillsConclusion[$skillId] = in_array($skillId, $userSkillsList);
+}
+
+$interbreadcrumb[] = [
+    'url' => Category::getUrl(),
+    'name' => get_lang('Gradebook'),
+];
+$interbreadcrumb[] = [
+    'url' => api_get_path(WEB_CODE_PATH).'gradebook/gradebook_display_summary.php?'.api_get_cidreq().'&selectcat='.$categoryId,
+    'name' => get_lang('GradebookListOfStudentsReports'),
+];
+
+$url = api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=assign_user_to_skill';
+
+$template = new Template(get_lang('SkillUserList'));
+$template->assign('conclusion_list', $uniqueSkillsConclusion);
+$template->assign('skills', $uniqueSkills);
+$template->assign('items', $itemsPerSkill);
+$template->assign('user', $userInfo);
+$template->assign('course_id', api_get_course_int_id());
+$template->assign('session_id', api_get_session_id());
+$template->assign('assign_user_url', $url);
+
+$templateName = $template->get_template('gradebook/skill_rel_user.tpl');
+$content = $template->fetch($templateName);
+$template->assign('content', $content);
+$template->display_one_col_template();

+ 13 - 17
main/inc/ajax/announcement.ajax.php

@@ -34,25 +34,21 @@ switch ($action) {
 
             $list = explode(',', $_REQUEST['id']);
             foreach ($list as $itemId) {
-                if (!api_is_session_general_coach() ||
-                    api_is_element_in_the_session(TOOL_ANNOUNCEMENT, $itemId)
-                ) {
-                    if (!empty($groupId)) {
-                        $result = AnnouncementManager::get_by_id(
-                            api_get_course_int_id(),
-                            $itemId
-                        );
-                        if (!empty($result)) {
-                            $delete = true;
-                            if (!empty($groupId) && $isTutor) {
-                                if ($groupId != $result['to_group_id']) {
-                                    $delete = false;
-                                }
-                            }
-                            if ($delete) {
-                                AnnouncementManager::delete_announcement($courseInfo, $itemId);
+                if (!api_is_session_general_coach() || api_is_element_in_the_session(TOOL_ANNOUNCEMENT, $itemId)) {
+                    $result = AnnouncementManager::get_by_id(
+                        api_get_course_int_id(),
+                        $itemId
+                    );
+                    if (!empty($result)) {
+                        $delete = true;
+                        if (!empty($groupId) && $isTutor) {
+                            if ($groupId != $result['to_group_id']) {
+                                $delete = false;
                             }
                         }
+                        if ($delete) {
+                            AnnouncementManager::delete_announcement($courseInfo, $itemId);
+                        }
                     }
                 }
             }

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

@@ -118,6 +118,7 @@ switch ($action) {
                         get_lang('Uploaded')
                     );
                 } else {
+                    $json['name'] = isset($file['name']) ? $file['name'] : get_lang('Unknown');
                     $json['url'] = '';
                     $json['error'] = get_lang('Error');
                 }

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

@@ -536,8 +536,8 @@ switch ($action) {
             exit;
         }
 
-        $questionId = isset($_GET['question']) ? intval($_GET['question']) : 0;
-        $exerciseId = isset($_REQUEST['exercise']) ? intval($_REQUEST['exercise']) : 0;
+        $questionId = isset($_GET['question']) ? (int) $_GET['question'] : 0;
+        $exerciseId = isset($_REQUEST['exercise']) ? (int) $_REQUEST['exercise'] : 0;
 
         if (!$questionId || !$exerciseId) {
             break;
@@ -547,9 +547,15 @@ switch ($action) {
         $objExercise->read($exerciseId);
 
         $objQuestion = Question::read($questionId);
-        $objQuestion->get_question_type_name();
 
         echo '<p class="lead">'.$objQuestion->get_question_type_name().'</p>';
+        if ($objQuestion->type == FILL_IN_BLANKS) {
+            echo '<script>
+                $(function() {
+                    $(".selectpicker").selectpicker({});
+                });
+            </script>';
+        }
         ExerciseLib::showQuestion(
             $objExercise,
             $questionId,

+ 16 - 7
main/inc/ajax/extra_field.ajax.php

@@ -6,14 +6,26 @@ use Chamilo\CoreBundle\Entity\Tag;
 require_once __DIR__.'/../global.inc.php';
 
 $action = isset($_GET['a']) ? $_GET['a'] : '';
+$type = isset($_REQUEST['type']) ? $_REQUEST['type'] : null;
+$fieldId = isset($_REQUEST['field_id']) ? $_REQUEST['field_id'] : null;
 
 switch ($action) {
+    case 'delete_file':
+        api_protect_admin_script();
+
+        $itemId = isset($_REQUEST['item_id']) ? $_REQUEST['item_id'] : null;
+        $extraFieldValue = new ExtraFieldValue($type);
+        $data = $extraFieldValue->get_values_by_handler_and_field_id($itemId, $fieldId);
+        if (!empty($data) && isset($data['id']) && !empty($data['value'])) {
+            $extraFieldValue->deleteValuesByHandlerAndFieldAndValue($itemId, $data['field_id'], $data['value']);
+            echo 1;
+            break;
+        }
+        echo 0;
+        break;
     case 'get_second_select_options':
-        $type = isset($_REQUEST['type']) ? $_REQUEST['type'] : null;
-        $field_id = isset($_REQUEST['field_id']) ? $_REQUEST['field_id'] : null;
         $option_value_id = isset($_REQUEST['option_value_id']) ? $_REQUEST['option_value_id'] : null;
-
-        if (!empty($type) && !empty($field_id) && !empty($option_value_id)) {
+        if (!empty($type) && !empty($fieldId) && !empty($option_value_id)) {
             $field_options = new ExtraFieldOption($type);
             echo $field_options->get_second_select_field_options_by_field(
                 $option_value_id,
@@ -23,9 +35,6 @@ switch ($action) {
         break;
     case 'search_tags':
         header('Content-Type: application/json');
-
-        $type = isset($_REQUEST['type']) ? $_REQUEST['type'] : null;
-        $fieldId = isset($_REQUEST['field_id']) ? $_REQUEST['field_id'] : null;
         $tag = isset($_REQUEST['q']) ? $_REQUEST['q'] : null;
         $result = [];
 

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

@@ -8,12 +8,17 @@ use ChamiloSession as Session;
  */
 require_once __DIR__.'/../global.inc.php';
 api_protect_course_script(true);
-$action = $_REQUEST['a'];
+
+$debug = false;
+$action = isset($_REQUEST['a']) ? $_REQUEST['a'] : '';
 
 $course_id = api_get_course_int_id();
-$tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
 $sessionId = api_get_session_id();
 
+if ($debug) {
+    error_log('----------lp.ajax-------------- action '.$action);
+}
+
 switch ($action) {
     case 'get_documents':
         $courseInfo = api_get_course_info();
@@ -102,6 +107,8 @@ switch ($action) {
                 }
             }
 
+            $table = Database::get_course_table(TABLE_LP_ITEM);
+
             foreach ($LP_item_list->list as $LP_item) {
                 $params = [];
                 $params['display_order'] = $LP_item->display_order;
@@ -110,7 +117,7 @@ switch ($action) {
                 $params['parent_item_id'] = $LP_item->parent_item_id;
 
                 Database::update(
-                    $tbl_lp_item,
+                    $table,
                     $params,
                     [
                         'id = ? AND c_id = ? ' => [
@@ -289,6 +296,23 @@ switch ($action) {
 
         echo json_encode($position);
 
+        break;
+    case 'get_parent_names':
+        $newItemId = isset($_GET['new_item']) ? intval($_GET['new_item']) : 0;
+
+        if (!$newItemId) {
+            break;
+        }
+
+        /** @var \learnpath $lp */
+        $lp = Session::read('oLP');
+        $parentNames = $lp->getCurrentItemParentNames($newItemId);
+        $response = '';
+        foreach ($parentNames as $parentName) {
+            $response .= '<p class="h5 hidden-xs hidden-md">'.$parentName.'</p>';
+        }
+
+        echo $response;
         break;
     default:
         echo '';

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

@@ -1194,6 +1194,10 @@ switch ($action) {
         $course = api_get_course_info();
         // Used inside ExerciseLib::get_exam_results_data()
         $documentPath = api_get_path(SYS_COURSE_PATH).$course['path']."/document";
+        $is_allowedToEdit = api_is_allowed_to_edit(null, true) ||
+            api_is_drh() ||
+            api_is_student_boss() ||
+            api_is_session_admin();
         if ($is_allowedToEdit || api_is_student_boss()) {
             $columns = [
                 'firstname',
@@ -1316,7 +1320,8 @@ switch ($action) {
             $courseInfo['code'],
             true,
             true,
-            $extraFieldsToAdd
+            $extraFieldsToAdd,
+            true
         );
         break;
     case 'get_hotpotatoes_exercise_results':

+ 107 - 18
main/inc/ajax/skill.ajax.php

@@ -1,5 +1,6 @@
 <?php
 /* For licensing terms, see /license.txt */
+
 /**
  * Responses to AJAX calls.
  */
@@ -20,12 +21,12 @@ switch ($action) {
     case 'add':
         if (api_is_platform_admin() || api_is_drh()) {
             if (isset($_REQUEST['id']) && !empty($_REQUEST['id'])) {
-                $skill_id = $skill->edit($_REQUEST);
+                $skillId = $skill->edit($_REQUEST);
             } else {
-                $skill_id = $skill->add($_REQUEST);
+                $skillId = $skill->add($_REQUEST);
             }
         }
-        echo $skill_id;
+        echo $skillId;
         break;
     case 'delete_skill':
         if (api_is_platform_admin() || api_is_drh()) {
@@ -73,10 +74,10 @@ switch ($action) {
         echo json_encode($return);
         break;
     case 'get_course_info_popup':
-        $course_info = api_get_course_info($_REQUEST['code']);
+        $courseInfo = api_get_course_info($_REQUEST['code']);
         $courses = CourseManager::processHotCourseItem(
             [
-                ['c_id' => $course_info['real_id']],
+                ['c_id' => $courseInfo['real_id']],
             ]
         );
         Display::display_no_header();
@@ -93,39 +94,39 @@ switch ($action) {
         }
         break;
     case 'get_skills_by_profile':
-        $skill_rel_profile = new SkillRelProfile();
+        $skillRelProfile = new SkillRelProfile();
         $profile_id = isset($_REQUEST['profile_id']) ? $_REQUEST['profile_id'] : null;
-        $skills = $skill_rel_profile->getSkillsByProfile($profile_id);
+        $skills = $skillRelProfile->getSkillsByProfile($profile_id);
         echo json_encode($skills);
         break;
     case 'get_saved_profiles':
-        $skill_profile = new SkillProfile();
-        $profiles = $skill_profile->get_all();
+        $skillProfile = new SkillProfile();
+        $profiles = $skillProfile->get_all();
         Display::display_no_header();
         Display::$global_template->assign('profiles', $profiles);
         $template = Display::$global_template->get_template('skill/profile_item.tpl');
         echo Display::$global_template->fetch($template);
         break;
     case 'get_skills':
-        $load_user_data = isset($_REQUEST['load_user_data']) ? $_REQUEST['load_user_data'] : null;
+        $loadUserData = isset($_REQUEST['load_user_data']) ? $_REQUEST['load_user_data'] : null;
         $id = intval($_REQUEST['id']);
-        $skills = $skill->get_all($load_user_data, false, $id);
+        $skills = $skill->get_all($loadUserData, false, $id);
         echo json_encode($skills);
         break;
     case 'get_skill_info':
         $id = isset($_REQUEST['id']) ? $_REQUEST['id'] : null;
-        $skill_info = $skill->getSkillInfo($id);
-        echo json_encode($skill_info);
+        $skillInfo = $skill->getSkillInfo($id);
+        echo json_encode($skillInfo);
         break;
     case 'get_skill_course_info':
         $id = isset($_REQUEST['id']) ? $_REQUEST['id'] : null;
-        $skill_info = $skill->getSkillInfo($id);
+        $skillInfo = $skill->getSkillInfo($id);
         $courses = $skill->getCoursesBySkill($id);
         $sessions = $skill->getSessionsBySkill($id);
         $html = '';
         if (!empty($courses) || !empty($sessions)) {
             Display::display_no_header();
-            Display::$global_template->assign('skill', $skill_info);
+            Display::$global_template->assign('skill', $skillInfo);
             Display::$global_template->assign('courses', $courses);
             Display::$global_template->assign('sessions', $sessions);
             $template = Display::$global_template->get_template('skill/skill_info.tpl');
@@ -218,7 +219,6 @@ switch ($action) {
                 $user_info = api_get_user_info($user['user_id']);
                 $user_list[$user['user_id']]['user'] = $user_info;
                 $my_user_skills = $skill_rel_user->getUserSkills($user['user_id']);
-
                 $user_skill_list = [];
                 foreach ($my_user_skills as $skill_item) {
                     $user_skill_list[] = $skill_item['skill_id'];
@@ -349,14 +349,36 @@ switch ($action) {
             ]
         );
         $returnSkills = [];
-
         foreach ($skills as $skill) {
             $returnSkills[] = [
                 'id' => $skill['id'],
                 'text' => $skill['name'],
             ];
         }
+        echo json_encode([
+            'items' => $returnSkills,
+        ]);
+        break;
+    case 'search_skills_in_course':
+        $courseId = isset($_REQUEST['course_id']) ? (int) $_REQUEST['course_id'] : 0;
+        $sessionId = isset($_REQUEST['session_id']) ? (int) $_REQUEST['session_id'] : null;
 
+        if (empty($courseId)) {
+            exit;
+        }
+        $em = Database::getManager();
+        $skills = $em->getRepository('ChamiloSkillBundle:SkillRelCourse')->findBy(
+            ['course' => $courseId, 'session' => $sessionId]
+        );
+
+        $returnSkills = [];
+        /** @var \Chamilo\SkillBundle\Entity\SkillRelCourse $skill */
+        foreach ($skills as $skill) {
+            $returnSkills[] = [
+                'id' => $skill->getSkill()->getId(),
+                'text' => $skill->getSkill()->getName(),
+            ];
+        }
         echo json_encode([
             'items' => $returnSkills,
         ]);
@@ -378,6 +400,7 @@ switch ($action) {
         $userId = isset($_REQUEST['user_id']) ? (int) $_REQUEST['user_id'] : 0;
         $courseId = isset($_REQUEST['course_id']) ? (int) $_REQUEST['course_id'] : 0;
         $sessionId = isset($_REQUEST['session_id']) ? (int) $_REQUEST['session_id'] : 0;
+        $resultId = isset($_REQUEST['result_id']) ? (int) $_REQUEST['result_id'] : 0;
 
         if (!empty($typeId) && !empty($itemId) && !empty($skillId) && !empty($userId) && !empty($courseId)) {
             $em = Database::getManager();
@@ -392,7 +415,6 @@ switch ($action) {
             }
 
             $session = $em->getRepository('ChamiloCoreBundle:Session')->find($sessionId);
-
             /** @var \Chamilo\SkillBundle\Entity\SkillRelItem $skillRelItem */
             $skillRelItem = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findOneBy(
                 ['itemId' => $itemId, 'itemType' => $typeId, 'skill' => $skillId]
@@ -413,6 +435,7 @@ switch ($action) {
                     $skillRelItemRelUser
                         ->setUser($user)
                         ->setSkillRelItem($skillRelItem)
+                        ->setResultId($resultId)
                         ->setCreatedBy($creatorId)
                         ->setUpdatedBy($creatorId)
                     ;
@@ -423,6 +446,72 @@ switch ($action) {
             echo Skill::getUserSkillStatusLabel($skillRelItem, $skillRelItemRelUser, false);
         }
         break;
+    case 'assign_user_to_skill':
+        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
+        if (empty($allowSkillInTools)) {
+            exit;
+        }
+
+        if (!api_is_allowed_to_edit()) {
+            exit;
+        }
+
+        $skillId = isset($_REQUEST['skill_id']) ? (int) $_REQUEST['skill_id'] : 0;
+        $userId = isset($_REQUEST['user_id']) ? (int) $_REQUEST['user_id'] : 0;
+        $courseId = isset($_REQUEST['course_id']) ? (int) $_REQUEST['course_id'] : 0;
+        $sessionId = isset($_REQUEST['session_id']) ? (int) $_REQUEST['session_id'] : null;
+
+        if (empty($skillId) || empty($userId)) {
+            exit;
+        }
+
+        $em = Database::getManager();
+        $skillRepo = $em->getRepository('ChamiloCoreBundle:Skill');
+        $skill = $skillRepo->find($skillId);
+        $user = api_get_user_entity($userId);
+
+        if (empty($skill) || empty($user)) {
+            exit;
+        }
+
+        $skillUserRepo = $em->getRepository('ChamiloCoreBundle:SkillRelUser');
+        $criteria = [
+            'user' => $user,
+            'skill' => $skill,
+        ];
+        $skillRelUsers = $skillUserRepo->findBy($criteria);
+        if (empty($skillRelUsers)) {
+            $skillUser = new \Chamilo\CoreBundle\Entity\SkillRelUser();
+            $skillUser->setUser($user);
+            $skillUser->setSkill($skill);
+            /*if ($showLevels) {
+                $level = $skillLevelRepo->find(intval($values['acquired_level']));
+                $skillUser->setAcquiredLevel($level);
+            }*/
+
+            $course = api_get_course_entity($courseId);
+            $skillUser->setCourse($course);
+            if (!empty($sessionId)) {
+                $session = $em->getRepository('ChamiloCoreBundle:Session')->find($sessionId);
+                $skillUser->setSession($session);
+            }
+
+            $skillUser->setArgumentation('');
+            $skillUser->setArgumentationAuthorId(api_get_user_id());
+            $skillUser->setAcquiredSkillAt(new DateTime());
+            $skillUser->setAssignedBy(0);
+            $em->persist($skillUser);
+            $em->flush();
+            $result = 'success';
+        } else {
+            foreach ($skillRelUsers as $skillRelUser) {
+                $em->remove($skillRelUser);
+            }
+            $em->flush();
+            $result = 'danger';
+        }
+        echo $result;
+        break;
     default:
         echo '';
 }

+ 7 - 8
main/lp/aiccItem.class.php

@@ -36,8 +36,8 @@ class aiccItem extends learnpathItem
      * Class constructor. Depending of the type of construction called ('db' or 'manifest'), will create a scormItem
      * object from database records or from the array given as second parameter.
      *
-     * @param	string	Type of construction needed ('db' or 'config', default = 'config')
-     * @param	mixed	Depending on the type given, DB id for the lp_item or parameters array
+     * @param    string    Type of construction needed ('db' or 'config', default = 'config')
+     * @param    mixed    Depending on the type given, DB id for the lp_item or parameters array
      */
     public function __construct($type = 'config', $params = [], $course_id = null)
     {
@@ -50,7 +50,6 @@ class aiccItem extends learnpathItem
                     // no break
                 case 'config': // Do the same as the default.
                 default:
-                    //if($first_item->type == XML_ELEMENT_NODE) this is already check prior to the call to this function
                     foreach ($params as $a => $value) {
                         switch ($a) {
                             case 'system_id':
@@ -109,10 +108,10 @@ class aiccItem extends learnpathItem
     /**
      * Builds a flat list with the current item and calls itself recursively on all children.
      *
-     * @param	array	Reference to the array to complete with the current item
-     * @param	int	Optional absolute order (pointer) of the item in this learning path
-     * @param	int	Optional relative order of the item at this level
-     * @param	int	Optional level. If not given, assumes it's level 0
+     * @param    array    Reference to the array to complete with the current item
+     * @param    int    Optional absolute order (pointer) of the item in this learning path
+     * @param    int    Optional relative order of the item at this level
+     * @param    int    Optional level. If not given, assumes it's level 0
      */
     public function get_flat_list(&$list, &$abs_order, $rel_order = 1, $level = 0)
     {
@@ -141,7 +140,7 @@ class aiccItem extends learnpathItem
     /**
      * Save function. Uses the parent save function and adds a layer for AICC.
      *
-     * @param	bool	Save from URL params (1) or from object attributes (0)
+     * @param    bool    Save from URL params (1) or from object attributes (0)
      */
     public function save($from_outside = true, $prereqs_complete = false)
     {

+ 1 - 0
main/lp/aicc_hacp.php

@@ -269,6 +269,7 @@ if (!empty($_REQUEST['command'])) {
 }
 
 Session::write('lpobject', serialize($oLP));
+Session::write('oLP', $oLP);
 session_write_close();
 // Content type must be text/plain.
 header('Content-type: text/plain');

File diff suppressed because it is too large
+ 153 - 183
main/lp/learnpath.class.php


+ 143 - 150
main/lp/learnpathItem.class.php

@@ -236,20 +236,6 @@ class learnpathItem
         if (($index + 1) > $this->interactions_count) {
             $this->interactions_count = $index + 1;
         }
-        /*
-        if (is_array($this->interactions[$index]) && count($this->interactions[$index]) > 0) {
-            $this->interactions[$index] = $params;
-            return false;
-        } else {
-            if (count($params)==8) {
-            // We rely on the calling script to provide parameters in the right order.
-                $this->interactions[$index] = $params;
-                return true;
-            } else {
-                return false;
-            }
-        }
-        */
     }
 
     /**
@@ -312,7 +298,6 @@ class learnpathItem
         }
         $lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW);
         $lp_item = Database::get_course_table(TABLE_LP_ITEM);
-
         $course_id = api_get_course_int_id();
 
         $sql = "DELETE FROM $lp_item_view
@@ -768,10 +753,10 @@ class learnpathItem
     public function get_lesson_mode()
     {
         $mode = 'normal';
-        if ($this->get_prevent_reinit() != 0) { // If prevent_reinit == 0
+        if ($this->get_prevent_reinit() != 0) {
+            // If prevent_reinit == 0
             $my_status = $this->get_status();
-            if ($my_status != $this->possible_status[0]
-                    && $my_status != $this->possible_status[1]) {
+            if ($my_status != $this->possible_status[0] && $my_status != $this->possible_status[1]) {
                 $mode = 'review';
             }
         }
@@ -1793,9 +1778,8 @@ class learnpathItem
      */
     public function get_terms()
     {
-        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
-        $course_id = api_get_course_int_id();
-        $sql = "SELECT * FROM $lp_item
+        $table = Database::get_course_table(TABLE_LP_ITEM);
+        $sql = "SELECT * FROM $table
                 WHERE iid = ".intval($this->db_id);
         $res = Database::query($sql);
         $row = Database::fetch_array($res);
@@ -1827,7 +1811,8 @@ class learnpathItem
      */
     public function get_total_time()
     {
-        if (self::DEBUG > 0) {
+        $debug = self::DEBUG;
+        if ($debug) {
             error_log(
                 'learnpathItem::get_total_time() for item '.$this->db_id.
                 ' - Initially, current_start_time = '.$this->current_start_time.
@@ -1837,7 +1822,7 @@ class learnpathItem
         }
         if ($this->current_start_time == 0) {
             // Shouldn't be necessary thanks to the open() method.
-            if (self::DEBUG > 2) {
+            if ($debug) {
                 error_log(
                     'learnpathItem::get_total_time() - Current start time was empty',
                     0
@@ -1849,7 +1834,7 @@ class learnpathItem
         if (time() < $this->current_stop_time ||
             $this->current_stop_time == 0
         ) {
-            if (self::DEBUG > 2) {
+            if ($debug) {
                 error_log(
                     'learnpathItem::get_total_time() - Current stop time was '
                     .'greater than the current time or was empty',
@@ -1865,7 +1850,7 @@ class learnpathItem
         $time = $this->current_stop_time - $this->current_start_time;
 
         if ($time < 0) {
-            if (self::DEBUG > 2) {
+            if ($debug) {
                 error_log(
                     'learnpathItem::get_total_time() - Time smaller than 0. Returning 0',
                     0
@@ -1875,7 +1860,7 @@ class learnpathItem
             return 0;
         } else {
             $time = $this->fixAbusiveTime($time);
-            if (self::DEBUG > 2) {
+            if ($debug) {
                 error_log(
                     'Current start time = '.$this->current_start_time.', current stop time = '.
                     $this->current_stop_time.' Returning '.$time."-----------\n"
@@ -2030,6 +2015,8 @@ class learnpathItem
     /**
      * Opens/launches the item. Initialises runtime values.
      *
+     * @param bool $allow_new_attempt
+     *
      * @return bool true on success, false on failure
      */
     public function open($allow_new_attempt = false)
@@ -2505,7 +2492,9 @@ class learnpathItem
 
                                                     if (isset($minScore) && isset($minScore)) {
                                                         // Taking min/max prerequisites values see BT#5776
-                                                        if ($quiz['exe_result'] >= $minScore && $quiz['exe_result'] <= $maxScore) {
+                                                        if ($quiz['exe_result'] >= $minScore &&
+                                                            $quiz['exe_result'] <= $maxScore
+                                                        ) {
                                                             $returnstatus = true;
                                                         } else {
                                                             $this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
@@ -2513,7 +2502,9 @@ class learnpathItem
                                                         }
                                                     } else {
                                                         // Classic way
-                                                        if ($quiz['exe_result'] >= $items[$refs_list[$prereqs_string]]->get_mastery_score()) {
+                                                        if ($quiz['exe_result'] >=
+                                                            $items[$refs_list[$prereqs_string]]->get_mastery_score()
+                                                        ) {
                                                             $returnstatus = true;
                                                         } else {
                                                             $this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
@@ -2577,7 +2568,6 @@ class learnpathItem
                                     } else {
                                         $status = $items[$refs_list[$prereqs_string]]->get_status(false);
                                         $returnstatus = $status == $this->possible_status[2] || $status == $this->possible_status[3];
-
                                         if (!$returnstatus) {
                                             if (self::DEBUG > 1) {
                                                 error_log(
@@ -2623,9 +2613,7 @@ class learnpathItem
                                                         lp_item_id = '.$refs_list[$prereqs_string].'
                                                     LIMIT 0, 1';
                                             $rs_lp = Database::query($sql);
-                                            $status_array = Database::fetch_row(
-                                                $rs_lp
-                                            );
+                                            $status_array = Database::fetch_row($rs_lp);
                                             $status = $status_array[0];
 
                                             $returnstatus = ($status == $this->possible_status[2]) || ($status == $this->possible_status[3]);
@@ -2791,8 +2779,6 @@ class learnpathItem
             //$this->current_score = 0;
             $this->current_start_time = 0;
             $this->current_stop_time = 0;
-            //$this->current_data = '';scorm
-            //$this->status = $this->possible_status[0];
             $this->interactions_count = $this->get_interactions_count(true);
         }
 
@@ -2810,7 +2796,8 @@ class learnpathItem
      */
     public function save($from_outside = true, $prereqs_complete = false)
     {
-        if (self::DEBUG > 0) {
+        $debug = self::DEBUG;
+        if ($debug) {
             error_log('learnpathItem::save()', 0);
         }
         // First check if parameters passed via GET can be saved here
@@ -2821,7 +2808,7 @@ class learnpathItem
                 $status != $this->possible_status[0] && // not attempted
                 $status != $this->possible_status[1]    //incomplete
             ) {
-                if (self::DEBUG > 1) {
+                if ($debug) {
                     error_log(
                         'learnpathItem::save() - save reinit blocked by setting',
                         0
@@ -2830,7 +2817,7 @@ class learnpathItem
                 // Do nothing because the status has already been set. Don't allow it to change.
                 // TODO: Check there isn't a special circumstance where this should be saved.
             } else {
-                if (self::DEBUG > 1) {
+                if ($debug) {
                     error_log(
                         'learnpathItem::save() - SCORM save request received',
                         0
@@ -2839,7 +2826,7 @@ class learnpathItem
 
                 // Get all new settings from the URL
                 if ($from_outside) {
-                    if (self::DEBUG > 1) {
+                    if ($debug) {
                         error_log(
                             'learnpathItem::save() - Getting item data from outside',
                             0
@@ -2849,7 +2836,7 @@ class learnpathItem
                         switch ($param) {
                             case 'score':
                                 $this->set_score($value);
-                                if (self::DEBUG > 2) {
+                                if ($debug) {
                                     error_log(
                                         'learnpathItem::save() - setting score to '.$value,
                                         0
@@ -2858,7 +2845,7 @@ class learnpathItem
                                 break;
                             case 'max':
                                 $this->set_max_score($value);
-                                if (self::DEBUG > 2) {
+                                if ($debug) {
                                     error_log(
                                         'learnpathItem::save() - setting view_max_score to '.$value,
                                         0
@@ -2867,7 +2854,7 @@ class learnpathItem
                                 break;
                             case 'min':
                                 $this->min_score = $value;
-                                if (self::DEBUG > 2) {
+                                if ($debug) {
                                     error_log(
                                         'learnpathItem::save() - setting min_score to '.$value,
                                         0
@@ -2877,7 +2864,7 @@ class learnpathItem
                             case 'lesson_status':
                                 if (!empty($value)) {
                                     $this->set_status($value);
-                                    if (self::DEBUG > 2) {
+                                    if ($debug) {
                                         error_log(
                                             'learnpathItem::save() - setting status to '.$value,
                                             0
@@ -2887,7 +2874,7 @@ class learnpathItem
                                 break;
                             case 'time':
                                 $this->set_time($value);
-                                if (self::DEBUG > 2) {
+                                if ($debug) {
                                     error_log(
                                         'learnpathItem::save() - setting time to '.$value,
                                         0
@@ -2896,7 +2883,7 @@ class learnpathItem
                                 break;
                             case 'suspend_data':
                                 $this->current_data = $value;
-                                if (self::DEBUG > 2) {
+                                if ($debug) {
                                     error_log(
                                         'learnpathItem::save() - setting suspend_data to '.$value,
                                         0
@@ -2905,7 +2892,7 @@ class learnpathItem
                                 break;
                             case 'lesson_location':
                                 $this->set_lesson_location($value);
-                                if (self::DEBUG > 2) {
+                                if ($debug) {
                                     error_log(
                                         'learnpathItem::save() - setting lesson_location to '.$value,
                                         0
@@ -2914,7 +2901,7 @@ class learnpathItem
                                 break;
                             case 'core_exit':
                                 $this->set_core_exit($value);
-                                if (self::DEBUG > 2) {
+                                if ($debug) {
                                     error_log(
                                         'learnpathItem::save() - setting core_exit to '.$value,
                                         0
@@ -2922,28 +2909,16 @@ class learnpathItem
                                 }
                                 break;
                             case 'interactions':
-                                //$interactions = unserialize($value);
-                                //foreach($interactions as $interaction){
-                                //	;
-                                //}
                                 break;
                             case 'objectives':
                                 break;
-                            //case 'maxtimeallowed':
-                            //$this->set_max_time_allowed($value);
-                            //break;
-                            /*
-                            case 'objectives._count':
-                                $this->attempt_id = $value;
-                                break;
-                            */
                             default:
                                 // Ignore.
                                 break;
                         }
                     }
                 } else {
-                    if (self::DEBUG > 1) {
+                    if ($debug) {
                         error_log(
                             'learnpathItem::save() - Using inside item status',
                             0
@@ -2955,6 +2930,9 @@ class learnpathItem
         } else {
             // If not SCO, such messages should not be expected.
             $type = strtolower($this->type);
+            if ($debug) {
+                error_log("type: $type");
+            }
             switch ($type) {
                 case 'asset':
                     if ($prereqs_complete) {
@@ -2976,7 +2954,7 @@ class learnpathItem
             }
         }
 
-        if (self::DEBUG > 1) {
+        if ($debug) {
             error_log(
                 'New LP - End of learnpathItem::save() - Calling write_to_db()',
                 0
@@ -3364,7 +3342,6 @@ class learnpathItem
     public function set_terms($terms)
     {
         global $charset;
-        $course_id = api_get_course_int_id();
         $lp_item = Database::get_course_table(TABLE_LP_ITEM);
         $a_terms = preg_split('/,/', $terms);
         $i_terms = preg_split('/,/', $this->get_terms());
@@ -3406,27 +3383,26 @@ class learnpathItem
      * often give it as 00:00:00.0000.
      *
      * @param    string    Time as given by SCORM
+     * @param string $format
      */
     public function set_time($scorm_time, $format = 'scorm')
     {
         $debug = self::DEBUG;
-        if ($debug > 0) {
-            error_log('learnpathItem::set_time('.$scorm_time.')', 0);
+        if ($debug) {
+            error_log("learnpathItem::set_time($scorm_time, $format)");
+            error_log("this->type: ".$this->type);
+            error_log("this->current_start_time: ".$this->current_start_time);
         }
 
         if ($scorm_time == '0' &&
             $this->type != 'sco' &&
             $this->current_start_time != 0
         ) {
-            $my_time = time() - $this->current_start_time;
-            if ($my_time > 0) {
-                $this->update_time($my_time);
-                if ($debug > 0) {
-                    error_log(
-                        'learnpathItem::set_time('.$scorm_time.') - '.
-                            'found asset - set time to '.$my_time,
-                        0
-                    );
+            $myTime = time() - $this->current_start_time;
+            if ($myTime > 0) {
+                $this->update_time($myTime);
+                if ($debug) {
+                    error_log('found asset - set time to '.$myTime);
                 }
             }
         } else {
@@ -3443,11 +3419,11 @@ class learnpathItem
                         $min = $res[2];
                         $sec = $res[3];
                         // Getting total number of seconds spent.
-                        $total_sec = $hour * 3600 + $min * 60 + $sec;
-                        if ($debug > 0) {
-                            error_log("total_sec : $total_sec");
+                        $totalSec = $hour * 3600 + $min * 60 + $sec;
+                        if ($debug) {
+                            error_log("totalSec : $totalSec");
                         }
-                        $this->scorm_update_time($total_sec);
+                        $this->scorm_update_time($totalSec);
                     }
                     break;
                 case 'int':
@@ -3540,42 +3516,20 @@ class learnpathItem
     /**
      * Updates the time info according to the given session_time.
      *
-     * @param int $total_sec Time in seconds
+     * @param int $totalSec Time in seconds
      */
-    public function update_time($total_sec = 0)
+    public function update_time($totalSec = 0)
     {
         if (self::DEBUG > 0) {
-            error_log('learnpathItem::update_time('.$total_sec.')', 0);
+            error_log('learnpathItem::update_time('.$totalSec.')');
         }
-        if ($total_sec >= 0) {
+        if ($totalSec >= 0) {
             // Getting start time from finish time. The only problem in the calculation is it might be
             // modified by the scripts processing time.
             $now = time();
-            $start = $now - $total_sec;
+            $start = $now - $totalSec;
             $this->current_start_time = $start;
             $this->current_stop_time = $now;
-            /*if (empty($this->current_start_time)) {
-                $this->current_start_time = $start;
-                $this->current_stop_time  = $now;
-            } else {
-                //if ($this->current_stop_time != $this->current_start_time) {
-                    // If the stop time has already been set before to something else
-                    // than the start time, add the given time to what's already been
-                    // recorder.
-                    // This is the SCORM way of doing things, because the time comes from
-                    // core.session_time, not core.total_time
-                    // UPDATE: adding time to previous time is only done on SCORM's finish()
-                    // call, not normally, so for now ignore this section.
-                    //$this->current_stop_time = $this->current_stop_time + $stop;
-                    //error_log('New LP - Adding '.$stop.' seconds - now '.$this->current_stop_time, 0);
-                //} else {
-                    // If no previous stop time set, use the one just calculated now from
-                    // start time.
-                    //$this->current_start_time = $start;
-                    //$this->current_stop_time  = $now;
-                    //error_log('New LP - Setting '.$stop.' seconds - now '.$this->current_stop_time, 0);
-                //}
-            }*/
         }
     }
 
@@ -3588,7 +3542,7 @@ class learnpathItem
     public function scorm_update_time($total_sec = 0)
     {
         $debug = self::DEBUG;
-        if ($debug > 0) {
+        if ($debug) {
             error_log('learnpathItem::scorm_update_time()');
             error_log("total_sec: $total_sec");
         }
@@ -3612,7 +3566,7 @@ class learnpathItem
         } else {
             $total_time = $row['total_time'];
         }
-        if ($debug > 0) {
+        if ($debug) {
             error_log("Original total_time: $total_time");
         }
 
@@ -3627,13 +3581,15 @@ class learnpathItem
         }
 
         // Step 2.1 : if normal mode total_time = total_time + total_sec
-        if ($accumulateScormTime != 0) {
+        if ($this->type == 'sco' && $accumulateScormTime != 0) {
+            if ($debug) {
+                error_log("accumulateScormTime is on. total_time modified: $total_time + $total_sec");
+            }
             $total_time += $total_sec;
         } else {
             // Step 2.2 : if not cumulative mode total_time = total_time - last_update + total_sec
             $total_sec = $this->fixAbusiveTime($total_sec);
-
-            if ($debug > 0) {
+            if ($debug) {
                 error_log("after fix abusive: $total_sec");
                 error_log("total_time: $total_time");
                 error_log("this->last_scorm_session_time: ".$this->last_scorm_session_time);
@@ -3647,7 +3603,7 @@ class learnpathItem
             }
         }
 
-        if ($debug > 0) {
+        if ($debug) {
             error_log("accumulate_scorm_time: $accumulateScormTime");
             error_log("total_time modified: $total_time");
         }
@@ -3671,8 +3627,10 @@ class learnpathItem
                         lp_item_id = {$this->db_id} AND 
                         lp_view_id = {$this->view_id} AND 
                         view_count = {$this->get_attempt_id()}";
-            if ($debug > 0) {
+            if ($debug) {
+                error_log('-------------total_time updated ------------------------');
                 error_log($sql);
+                error_log('-------------------------------------');
             }
             Database::query($sql);
         }
@@ -3786,13 +3744,14 @@ class learnpathItem
      */
     public function write_to_db()
     {
-        if (self::DEBUG > 0) {
+        $debug = self::DEBUG;
+        if ($debug) {
             error_log('learnpathItem::write_to_db()', 0);
         }
 
         // Check the session visibility.
         if (!api_is_allowed_to_session_edit()) {
-            if (self::DEBUG > 0) {
+            if ($debug) {
                 error_log('return false api_is_allowed_to_session_edit');
             }
 
@@ -3836,13 +3795,13 @@ class learnpathItem
            ($this->type == 'sco' && ($credit == 'no-credit' || $mode == 'review' || $mode == 'browse'))) &&
            ($this->seriousgame_mode != 1 && $this->type == 'sco')
         ) {
-            if (self::DEBUG > 1) {
+            if ($debug) {
                 error_log(
                     "This info shouldn't be saved as the credit or lesson mode info prevent it"
                 );
                 error_log(
                     'learnpathItem::write_to_db() - credit('.$credit.') or'.
-                        ' lesson_mode('.$mode.') prevent recording!',
+                    ' lesson_mode('.$mode.') prevent recording!',
                     0
                 );
             }
@@ -3870,7 +3829,7 @@ class learnpathItem
                     //"max_time_allowed" => ,
                     "lesson_location" => $this->lesson_location,
                 ];
-                if (self::DEBUG > 2) {
+                if ($debug) {
                     error_log(
                         'learnpathItem::write_to_db() - Inserting into item_view forced: '.print_r($params, 1),
                         0
@@ -3892,7 +3851,7 @@ class learnpathItem
                         lp_item_id = ".$this->db_id." AND
                         lp_view_id = ".$this->view_id." AND
                         view_count = ".intval($this->get_attempt_id());
-            if (self::DEBUG > 2) {
+            if ($debug) {
                 error_log(
                     'learnpathItem::write_to_db() - Querying item_view: '.$sql,
                     0
@@ -3917,7 +3876,7 @@ class learnpathItem
                     "lesson_location" => $this->lesson_location,
                 ];
 
-                if (self::DEBUG > 2) {
+                if ($debug) {
                     error_log(
                         'learnpathItem::write_to_db() - Inserting into item_view forced: '.print_r($params, 1),
                         0
@@ -3953,27 +3912,27 @@ class learnpathItem
                 } else {
                     // For all other content types...
                     if ($this->type == 'quiz') {
+                        if ($debug) {
+                            error_log("item is quiz:");
+                        }
                         $my_status = ' ';
                         $total_time = ' ';
                         if (!empty($_REQUEST['exeId'])) {
                             $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
-
-                            $safe_exe_id = intval($_REQUEST['exeId']);
-                            $sql = "SELECT start_date, exe_date
+                            $exeId = (int) $_REQUEST['exeId'];
+                            $sql = "SELECT exe_duration
                                     FROM $table
-                                    WHERE exe_id = $safe_exe_id";
+                                    WHERE exe_id = $exeId";
+                            if ($debug) {
+                                error_log($sql);
+                            }
                             $res = Database::query($sql);
-                            $row_dates = Database::fetch_array($res);
-
-                            $time_start_date = convert_sql_date(
-                                $row_dates['start_date']
-                            );
-                            $time_exe_date = convert_sql_date(
-                                $row_dates['exe_date']
-                            );
-                            $mytime = ((int) $time_exe_date - (int) $time_start_date);
-                            $mytime = $this->fixAbusiveTime($mytime);
-                            $total_time = " total_time = ".$mytime.", ";
+                            $exeRow = Database::fetch_array($res);
+                            $duration = $exeRow['exe_duration'];
+                            $total_time = " total_time = ".$duration.", ";
+                            if ($debug) {
+                                error_log("quiz: $total_time");
+                            }
                         }
                     } else {
                         $my_type_lp = learnpath::get_type_static($this->lp_id);
@@ -3989,6 +3948,9 @@ class learnpathItem
                         if ($this->seriousgame_mode == 1 && $this->type == 'sco') {
                             $total_time = " total_time = total_time +".$this->get_total_time().", ";
                             $my_status = " status = '".$this->get_status(false)."' ,";
+                            if ($debug) {
+                                error_log("seriousgame_mode time changed: $total_time");
+                            }
                         } elseif ($this->get_prevent_reinit() == 1) {
                             // Process of status verified into data base.
                             $sql = 'SELECT status FROM '.$item_view_table.'
@@ -4008,6 +3970,9 @@ class learnpathItem
                             ) {
                                 $total_time = " total_time = total_time +".$this->get_total_time().", ";
                                 $my_status = " status = '".$this->get_status(false)."' ,";
+                                if ($debug) {
+                                    error_log("get_prevent_reinit = 1 time changed: $total_time");
+                                }
                             } else {
                                 // Verified into database.
                                 if (!in_array($row_verified['status'], $case_completed) &&
@@ -4015,17 +3980,26 @@ class learnpathItem
                                 ) {
                                     $total_time = " total_time = total_time +".$this->get_total_time().", ";
                                     $my_status = " status = '".$this->get_status(false)."' ,";
+                                    if ($debug) {
+                                        error_log("total_time time changed case 1: $total_time");
+                                    }
                                 } elseif (in_array($row_verified['status'], $case_completed) &&
                                     $my_type_lp == 2 && $this->type != 'sco'
                                 ) {
                                     $total_time = " total_time = total_time +".$this->get_total_time().", ";
                                     $my_status = " status = '".$this->get_status(false)."' ,";
+                                    if ($debug) {
+                                        error_log("total_time time changed case 2: $total_time");
+                                    }
                                 } else {
                                     if (($my_type_lp == 3 && $this->type == 'au') ||
                                         ($my_type_lp == 1 && $this->type != 'dir')) {
                                         // Is AICC or Chamilo LP
                                         $total_time = " total_time = total_time + ".$this->get_total_time().", ";
                                         $my_status = " status = '".$this->get_status(false)."' ,";
+                                        if ($debug) {
+                                            error_log("total_time time changed case 3: $total_time");
+                                        }
                                     }
                                 }
                             }
@@ -4034,13 +4008,22 @@ class learnpathItem
                             if (in_array($this->get_status(false), $case_completed) && $my_type_lp == 2) {
                                 // Reset zero new attempt ?
                                 $my_status = " status = '".$this->get_status(false)."' ,";
+                                if ($debug) {
+                                    error_log("total_time time changed Multiple attempt case 1: $total_time");
+                                }
                             } elseif (!in_array($this->get_status(false), $case_completed) && $my_type_lp == 2) {
                                 $total_time = " total_time = ".$this->get_total_time().", ";
                                 $my_status = " status = '".$this->get_status(false)."' ,";
+                                if ($debug) {
+                                    error_log("total_time time changed Multiple attempt case 2: $total_time");
+                                }
                             } else {
                                 // It is chamilo LP.
                                 $total_time = " total_time = total_time +".$this->get_total_time().", ";
                                 $my_status = " status = '".$this->get_status(false)."' ,";
+                                if ($debug) {
+                                    error_log("total_time time changed Multiple attempt case 3: $total_time");
+                                }
                             }
 
                             // This code line fixes the problem of wrong status.
@@ -4064,6 +4047,10 @@ class learnpathItem
                                 } else {
                                     $total_time = " total_time = total_time + ".$this->get_total_time().", ";
                                 }
+
+                                if ($debug) {
+                                    error_log("total_time time my_type_lp: $total_time");
+                                }
                             }
                         }
                     }
@@ -4101,11 +4088,11 @@ class learnpathItem
                     }
                     $this->current_start_time = time();
                 }
-                if (self::DEBUG > 2) {
-                    error_log(
-                        'learnpathItem::write_to_db() - Updating item_view: '.$sql,
-                        0
-                    );
+                if ($debug) {
+                    error_log('-------------------------------------------');
+                    error_log('learnpathItem::write_to_db() - Updating item_view:');
+                    error_log($sql);
+                    error_log('-------------------------------------------');
                 }
                 Database::query($sql);
             }
@@ -4125,10 +4112,10 @@ class learnpathItem
                 if (Database::num_rows($res) > 0) {
                     $row = Database::fetch_array($res);
                     $lp_iv_id = $row[0];
-                    if (self::DEBUG > 2) {
+                    if ($debug) {
                         error_log(
                             'learnpathItem::write_to_db() - Got item_view_id '.
-                                $lp_iv_id.', now checking interactions ',
+                            $lp_iv_id.', now checking interactions ',
                             0
                         );
                     }
@@ -4223,7 +4210,7 @@ class learnpathItem
             }
         }
 
-        if (self::DEBUG > 2) {
+        if ($debug) {
             error_log('End of learnpathItem::write_to_db()', 0);
         }
 
@@ -4323,11 +4310,12 @@ class learnpathItem
             $course_info['code']
         );
 
+        $file_path = '';
         if (!empty($document_data)) {
             $file_path = basename($document_data['path']);
             // Store the mp3 file in the lp_item table.
-            $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
-            $sql = "UPDATE $tbl_lp_item SET
+            $table = Database::get_course_table(TABLE_LP_ITEM);
+            $sql = "UPDATE $table SET
                         audio = '".Database::escape_string($file_path)."'
                     WHERE iid = ".intval($this->db_id);
             Database::query($sql);
@@ -4345,12 +4333,11 @@ class learnpathItem
      */
     public function remove_audio()
     {
-        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
-        $course_id = api_get_course_int_id();
         if (empty($this->db_id)) {
             return false;
         }
-        $sql = "UPDATE $tbl_lp_item SET
+        $table = Database::get_course_table(TABLE_LP_ITEM);
+        $sql = "UPDATE $table SET
                 audio = ''
                 WHERE iid IN (".$this->db_id.")";
         Database::query($sql);
@@ -4556,7 +4543,7 @@ class learnpathItem
      */
     public function createForumThread($currentForumId)
     {
-        require_once api_get_path(SYS_CODE_PATH).'/forum/forumfunction.inc.php';
+        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
 
         $em = Database::getManager();
         $threadRepo = $em->getRepository('ChamiloCourseBundle:CForumThread');
@@ -4605,7 +4592,7 @@ class learnpathItem
      */
     public function dissociateForumThread($threadIid)
     {
-        $threadIid = intval($threadIid);
+        $threadIid = (int) $threadIid;
         $em = Database::getManager();
 
         $forumThread = $em->find('ChamiloCourseBundle:CForumThread', $threadIid);
@@ -4614,9 +4601,7 @@ class learnpathItem
             return false;
         }
 
-        $forumThread->setThreadTitle(
-            "{$this->get_title()} - {$this->db_id}"
-        );
+        $forumThread->setThreadTitle("{$this->get_title()} - {$this->db_id}");
         $forumThread->setLpItemId(0);
 
         $em->persist($forumThread);
@@ -4624,4 +4609,12 @@ class learnpathItem
 
         return true;
     }
+
+    /**
+     * @return int
+     */
+    public function getLastScormSessionTime()
+    {
+        return $this->last_scorm_session_time;
+    }
 }

+ 0 - 1
main/lp/lp_add_audio.php

@@ -119,7 +119,6 @@ $htmlHeadXtra[] = '<script type="text/javascript" src="'.api_get_path(WEB_LIBRAR
 $tpl = new Template(null);
 $tpl->assign('unique_file_id', api_get_unique_id());
 $tpl->assign('course_code', api_get_course_id());
-$tpl->assign('php_session_id', session_id());
 $tpl->assign('filename', $lp_item->get_title().'_nano.wav');
 $tpl->assign('enable_record_audio', api_get_setting('enable_record_audio') === 'true');
 $tpl->assign('cur_dir_path', '/audio');

+ 9 - 16
main/lp/lp_ajax_initialize.php

@@ -28,10 +28,10 @@ require_once __DIR__.'/../inc/global.inc.php';
  */
 function initialize_item($lp_id, $user_id, $view_id, $next_item)
 {
-    global $debug;
+    $debug = 0;
     $return = '';
-    if ($debug > 0) {
-        error_log('In initialize_item('.$lp_id.','.$user_id.','.$view_id.','.$next_item.')', 0);
+    if ($debug) {
+        error_log('In initialize_item('.$lp_id.','.$user_id.','.$view_id.','.$next_item.')');
     }
     /*$item_id may be one of:
      * -'next'
@@ -42,18 +42,18 @@ function initialize_item($lp_id, $user_id, $view_id, $next_item)
      */
     $mylp = learnpath::getLpFromSession(api_get_course_id(), $lp_id, $user_id);
     $mylp->set_current_item($next_item);
-    if ($debug > 1) {
-        error_log('In initialize_item() - new item is '.$next_item, 0);
+    if ($debug) {
+        error_log('In initialize_item() - new item is '.$next_item);
     }
     $mylp->start_current_item(true);
 
     if (is_object($mylp->items[$next_item])) {
-        if ($debug > 1) {
+        if ($debug) {
             error_log('In initialize_item - recovering existing item object '.$next_item, 0);
         }
         $mylpi = $mylp->items[$next_item];
     } else {
-        if ($debug > 1) {
+        if ($debug) {
             error_log('In initialize_item - generating new item object '.$next_item, 0);
         }
         $mylpi = new learnpathItem($next_item, $user_id);
@@ -141,10 +141,6 @@ function initialize_item($lp_id, $user_id, $view_id, $next_item)
      * -lms_view_id
      * -lms_user_id
      */
-    $mytotal = $mylp->getTotalItemsCountWithoutDirs();
-    $mycomplete = $mylp->get_complete_items_count();
-    $myprogress_mode = $mylp->get_progress_bar_mode();
-    $myprogress_mode = ($myprogress_mode == '' ? '%' : $myprogress_mode);
     $mynext = $mylp->get_next_item_id();
     $myprevious = $mylp->get_previous_item_id();
     $myitemtype = $mylpi->get_type();
@@ -152,9 +148,7 @@ function initialize_item($lp_id, $user_id, $view_id, $next_item)
     $mycredit = $mylpi->get_credit();
     $mylaunch_data = $mylpi->get_launch_data();
     $myinteractions_count = $mylpi->get_interactions_count();
-    $myobjectives_count = $mylpi->get_objectives_count();
     $mycore_exit = $mylpi->get_core_exit();
-
     $return .=
             "olms.lms_lp_id=".$lp_id.";".
             "olms.lms_item_id=".$next_item.";".
@@ -176,11 +170,10 @@ function initialize_item($lp_id, $user_id, $view_id, $next_item)
 
     $mylp->set_error_msg('');
     $mylp->prerequisites_match(); // Check the prerequisites are all complete.
-    if ($debug > 1) {
+    if ($debug) {
         error_log('Prereq_match() returned '.htmlentities($mylp->error), 0);
-    }
-    if ($debug > 1) {
         error_log("return = $return ");
+        error_log("mylp->lp_view_session_id: ".$mylp->lp_view_session_id);
     }
 
     return $return;

+ 3 - 1
main/lp/lp_ajax_save_item.php

@@ -60,11 +60,11 @@ function save_item(
     $userNavigatesAway = 0,
     $statusSignalReceived = 0
 ) {
-    //global $debug;
     $debug = 0;
     $return = null;
 
     if ($debug > 0) {
+        error_log('--------------------------------------');
         error_log('lp_ajax_save_item.php : save_item() params: ');
         error_log("item_id: $item_id");
         error_log("lp_id: $lp_id - user_id: - $user_id - view_id: $view_id - item_id: $item_id");
@@ -478,7 +478,9 @@ function save_item(
     $myLP->save_last();
 
     Session::write('lpobject', serialize($myLP));
+    Session::write('oLP', $myLP);
     if ($debug > 0) {
+        error_log("lp_view_session_id :".$myLP->lp_view_session_id);
         error_log('---------------- lp_ajax_save_item.php : save_item end ----- ');
     }
 

+ 1 - 15
main/lp/lp_ajax_start_timer.php

@@ -6,18 +6,4 @@
  *
  * @author Yannick Warnier <ywarnier@beeznest.org>
  */
-
-/**
- * Start a timer and hand it back to the JS by assigning the current time (of start) to
- * var asset_timer.
- *
- * @return string JavaScript time intializer
- */
-function start_timer()
-{
-    $time = time();
-
-    return $time; //"olms.asset_timer='$time'; olms.asset_timer_total = 0;";
-}
-
-echo start_timer();
+echo time();

+ 8 - 5
main/lp/lp_ajax_switch_item.php

@@ -52,7 +52,7 @@ function switch_item_details($lp_id, $user_id, $view_id, $current_item, $next_it
             $mylp->next();
             $new_item_id = $mylp->get_current_item_id();
             if ($debug > 1) {
-                error_log('In {next} - next item is '.$new_item_id.'(current: '.$current_item.')', 0);
+                error_log('In {next} - next item is '.$new_item_id.'(current: '.$current_item.')');
             }
             break;
         case 'previous':
@@ -60,7 +60,7 @@ function switch_item_details($lp_id, $user_id, $view_id, $current_item, $next_it
             $mylp->previous();
             $new_item_id = $mylp->get_current_item_id();
             if ($debug > 1) {
-                error_log('In {previous} - next item is '.$new_item_id.'(current: '.$current_item.')', 0);
+                error_log('In {previous} - next item is '.$new_item_id.'(current: '.$current_item.')');
             }
             break;
         case 'first':
@@ -68,7 +68,7 @@ function switch_item_details($lp_id, $user_id, $view_id, $current_item, $next_it
             $mylp->first();
             $new_item_id = $mylp->get_current_item_id();
             if ($debug > 1) {
-                error_log('In {first} - next item is '.$new_item_id.'(current: '.$current_item.')', 0);
+                error_log('In {first} - next item is '.$new_item_id.'(current: '.$current_item.')');
             }
             break;
         case 'last':
@@ -82,7 +82,7 @@ function switch_item_details($lp_id, $user_id, $view_id, $current_item, $next_it
             $new_item_id = $next_item;
             $mylp->set_current_item($new_item_id);
             if ($debug > 1) {
-                error_log('In {default} - next item is '.$new_item_id.'(current: '.$current_item.')', 0);
+                error_log('In {default} - next item is '.$new_item_id.'(current: '.$current_item.')');
             }
             break;
     }
@@ -123,7 +123,9 @@ function switch_item_details($lp_id, $user_id, $view_id, $current_item, $next_it
     $mylaunch_data = $mylpi->get_launch_data();
     /*
     if ($mylpi->get_type() == 'asset') {
-        // Temporary measure to save completion of an asset. Later on, Chamilo should trigger something on unload, maybe... (even though that would mean the last item cannot be completed)
+        // Temporary measure to save completion of an asset. Later on,
+        // Chamilo should trigger something on unload, maybe...
+        // (even though that would mean the last item cannot be completed)
         $mylesson_status = 'completed';
         $mylpi->set_status('completed');
         $mylpi->save();
@@ -229,6 +231,7 @@ function switch_item_details($lp_id, $user_id, $view_id, $current_item, $next_it
     // Save the new item ID for the exercise tool to use.
     Session::write('scorm_item_id', $new_item_id);
     Session::write('lpobject', serialize($mylp));
+    Session::write('oLP', $mylp);
 
     return $return;
 }

+ 1 - 0
main/lp/lp_ajax_switch_item_toc.php

@@ -161,6 +161,7 @@ function switch_item_toc($lpId, $userId, $viewId, $currentItem, $nextItem)
     }
     Session::write('scorm_item_id', $newItemId);
     Session::write('lpobject', serialize($myLP));
+    Session::write('oLP', $myLP);
 
     return $return;
 }

+ 8 - 6
main/lp/lp_content.php

@@ -14,13 +14,13 @@ require_once __DIR__.'/../inc/global.inc.php';
 
 $debug = 0;
 if ($debug > 0) {
-    error_log('New lp - In lp_content.php', 0);
+    error_log('New lp - In lp_content.php');
 }
 if (empty($lp_controller_touched)) {
     if ($debug > 0) {
-        error_log('New lp - In lp_content.php - Redirecting to lp_controller', 0);
+        error_log('New lp - In lp_content.php - Redirecting to lp_controller');
     }
-    header('location: lp_controller.php?action=content&lp_id='.intval($_REQUEST['lp_id']).'&item_id='.intval($_REQUEST['item_id']).'&'.api_get_cidreq());
+    header('Location: lp_controller.php?action=content&lp_id='.intval($_REQUEST['lp_id']).'&item_id='.intval($_REQUEST['item_id']).'&'.api_get_cidreq());
     exit;
 }
 
@@ -35,7 +35,9 @@ $lp_item_id = $learnPath->get_current_item_id();
  */
 $src = '';
 if ($debug > 0) {
-    error_log('New lp - In lp_content.php - Looking for file url', 0);
+    error_log('New lp - In lp_content.php - Looking for file url');
+    error_log("lp_type $lp_type");
+    error_log("lp_item_id $lp_item_id");
 }
 
 $list = $learnPath->get_toc();
@@ -93,7 +95,7 @@ if ($dir) {
 }
 
 if ($debug > 0) {
-    error_log('New lp - In lp_content.php - File url is '.$src, 0);
+    error_log('New lp - In lp_content.php - File url is '.$src);
 }
 $learnPath->set_previous_item($lp_item_id);
 
@@ -114,7 +116,7 @@ $save_setting = api_get_setting('show_navigation_menu');
 global $_setting;
 $_setting['show_navigation_menu'] = false;
 if ($debug > 0) {
-    error_log('New LP - In lp_content.php - Loading '.$src, 0);
+    error_log('New LP - In lp_content.php - Loading '.$src);
 }
 Session::write('oLP', $learnPath);
 header("Location: ".urldecode($src));

+ 54 - 50
main/lp/lp_controller.php

@@ -16,28 +16,18 @@ use ChamiloSession as Session;
 
 // Flag to allow for anonymous user - needs to be set before global.inc.php.
 $use_anonymous = true;
-
 $debug = 0;
-if ($debug > 0) {
-    error_log('New LP -+- Entered lp_controller.php -+- (action: '.$_REQUEST['action'].')', 0);
-}
 
-// Language files that needs to be included.
-if (isset($_GET['action'])) {
-    if ($_GET['action'] === 'export') {
-        // Only needed on export.
-        $language_file[] = 'hotspot';
-    }
+if ($debug) {
+    error_log('Entering lp_controller.php. Checking if LP exist in current session');
 }
 
-// Including the global initialization file.
 require_once __DIR__.'/../inc/global.inc.php';
 $current_course_tool = TOOL_LEARNPATH;
 $_course = api_get_course_info();
 
 $glossaryExtraTools = api_get_setting('show_glossary_in_extra_tools');
 $showGlossary = in_array($glossaryExtraTools, ['true', 'lp', 'exercise_and_lp']);
-
 if ($showGlossary) {
     if (api_get_setting('show_glossary_in_documents') === 'ismanual' ||
         api_get_setting('show_glossary_in_documents') === 'isautomatic'
@@ -219,7 +209,7 @@ if ($refresh == 1) {
 }
 
 if ($debug > 0) {
-    error_log('New LP - $myrefresh: '.$myrefresh);
+    error_log(' $myrefresh: '.$myrefresh);
 }
 
 if (!empty($_REQUEST['dialog_box'])) {
@@ -230,24 +220,27 @@ $lp_controller_touched = 1;
 $lp_found = false;
 $lpObject = Session::read('lpobject');
 if (!empty($lpObject)) {
-    if ($debug > 0) {
-        error_log('New LP - SESSION[lpobject] is defined', 0);
+    if ($debug) {
+        error_log(' SESSION[lpobject] is defined');
     }
     $oLP = unserialize($lpObject);
     if (isset($oLP) && is_object($oLP)) {
-        if ($debug > 0) {
-            error_log('New LP - oLP is object', 0);
+        if ($debug) {
+            error_log(' oLP is object');
         }
         if ($myrefresh == 1 ||
             empty($oLP->cc) ||
             $oLP->cc != api_get_course_id() ||
             $oLP->lp_view_session_id != $session_id
         ) {
-            if ($debug > 0) {
-                error_log('New LP - Course has changed, discard lp object');
-                error_log('New LP - $oLP->lp_view_session_id: '.$oLP->lp_view_session_id);
-                error_log('New LP - $oLP->cc: '.$oLP->cc);
+            if ($debug) {
+                error_log('Course has changed, discard lp object');
+                error_log('$oLP->lp_view_session_id: '.$oLP->lp_view_session_id);
+                error_log('api_get_session_id(): '.$session_id);
+                error_log('$oLP->cc: '.$oLP->cc);
+                error_log('api_get_course_id(): '.api_get_course_id());
             }
+
             if ($myrefresh == 1) {
                 $myrefresh_id = $oLP->get_id();
             }
@@ -260,21 +253,20 @@ if (!empty($lpObject)) {
         }
     }
 }
+if ($debug) {
+    error_log('$lp_found: '.$lp_found);
+}
 
 $course_id = api_get_course_int_id();
 
-if ($debug > 0) {
-    error_log('New LP - Passed data remains check', 0);
-}
-
 if (!$lp_found || (!empty($_REQUEST['lp_id']) && $_SESSION['oLP']->get_id() != $_REQUEST['lp_id'])) {
     if ($debug > 0) {
-        error_log('New LP - oLP is not object, has changed or refresh been asked, getting new', 0);
+        error_log(' oLP is not object, has changed or refresh been asked, getting new');
     }
     // Regenerate a new lp object? Not always as some pages don't need the object (like upload?)
     if (!empty($_REQUEST['lp_id']) || !empty($myrefresh_id)) {
         if ($debug > 0) {
-            error_log('New LP - lp_id is defined', 0);
+            error_log(' lp_id is defined');
         }
         // Select the lp in the database and check which type it is (scorm/dokeos/aicc) to generate the
         // right object.
@@ -288,7 +280,7 @@ if (!$lp_found || (!empty($_REQUEST['lp_id']) && $_SESSION['oLP']->get_id() != $
         if (is_numeric($lp_id)) {
             $sel = "SELECT iid, lp_type FROM $lp_table WHERE c_id = $course_id AND id = $lp_id";
             if ($debug > 0) {
-                error_log('New LP - querying '.$sel, 0);
+                error_log(' querying '.$sel);
             }
             $res = Database::query($sel);
             if (Database::num_rows($res)) {
@@ -296,7 +288,8 @@ if (!$lp_found || (!empty($_REQUEST['lp_id']) && $_SESSION['oLP']->get_id() != $
                 $lpIid = $row['iid'];
                 $type = $row['lp_type'];
                 if ($debug > 0) {
-                    error_log('New LP - found row - type '.$type.' - Calling constructor with '.api_get_course_id().' - '.$lp_id.' - '.api_get_user_id(), 0);
+                    error_log('Found row type '.$type);
+                    error_log('Calling constructor: '.api_get_course_id().' - '.$lp_id.' - '.api_get_user_id());
                 }
                 switch ($type) {
                     case 1:
@@ -304,7 +297,7 @@ if (!$lp_found || (!empty($_REQUEST['lp_id']) && $_SESSION['oLP']->get_id() != $
                         if ($oLP !== false) {
                             $lp_found = true;
                         } else {
-                            error_log($oLP->error, 0);
+                            error_log($oLP->error);
                         }
                         break;
                     case 2:
@@ -312,7 +305,7 @@ if (!$lp_found || (!empty($_REQUEST['lp_id']) && $_SESSION['oLP']->get_id() != $
                         if ($oLP !== false) {
                             $lp_found = true;
                         } else {
-                            error_log($oLP->error, 0);
+                            error_log($oLP->error);
                         }
                         break;
                     case 3:
@@ -320,7 +313,7 @@ if (!$lp_found || (!empty($_REQUEST['lp_id']) && $_SESSION['oLP']->get_id() != $
                         if ($oLP !== false) {
                             $lp_found = true;
                         } else {
-                            error_log($oLP->error, 0);
+                            error_log($oLP->error);
                         }
                         break;
                     default:
@@ -328,19 +321,19 @@ if (!$lp_found || (!empty($_REQUEST['lp_id']) && $_SESSION['oLP']->get_id() != $
                         if ($oLP !== false) {
                             $lp_found = true;
                         } else {
-                            error_log($oLP->error, 0);
+                            error_log($oLP->error);
                         }
                         break;
                 }
             }
         } else {
             if ($debug > 0) {
-                error_log('New LP - Request[lp_id] is not numeric', 0);
+                error_log(' Request[lp_id] is not numeric');
             }
         }
     } else {
         if ($debug > 0) {
-            error_log('New LP - Request[lp_id] and refresh_id were empty', 0);
+            error_log(' Request[lp_id] and refresh_id were empty');
         }
     }
     if ($lp_found) {
@@ -349,7 +342,7 @@ if (!$lp_found || (!empty($_REQUEST['lp_id']) && $_SESSION['oLP']->get_id() != $
 }
 
 if ($debug > 0) {
-    error_log('New LP - Passed oLP creation check', 0);
+    error_log('Passed oLP creation check', 0);
 }
 
 $is_allowed_to_edit = api_is_allowed_to_edit(false, true, false, false);
@@ -379,6 +372,10 @@ if (isset($_GET['isStudentView']) && $_GET['isStudentView'] == 'true') {
 
 $action = !empty($_REQUEST['action']) ? $_REQUEST['action'] : '';
 
+if ($debug) {
+    error_log('Entered lp_controller.php -+- (action: '.$action.')');
+}
+
 // format title to be displayed correctly if QUIZ
 $post_title = '';
 if (isset($_POST['title'])) {
@@ -394,10 +391,10 @@ if (isset($_POST['title'])) {
 
 $redirectTo = '';
 if ($debug > 0) {
-    error_log('New LP - action "'.$action.'" triggered');
+    error_log('action "'.$action.'" triggered');
     if (!$lp_found) {
         //check if the learnpath ID was defined, otherwise send back to list
-        error_log('New LP - No learnpath given');
+        error_log('No learnpath given');
     }
 }
 
@@ -1031,7 +1028,7 @@ switch ($action) {
 
             $extraFieldValue = new ExtraFieldValue('lp');
             $params = [
-                'lp_id' => $_SESSION['oLP']->id,
+                'lp_id' => $_SESSION['oLP']->lp_id,
             ];
             $extraFieldValue->saveFieldValues($_REQUEST);
 
@@ -1153,21 +1150,22 @@ switch ($action) {
         break;
     case 'content':
         if ($debug > 0) {
-            error_log('New LP - Item id is '.intval($_GET['item_id']), 0);
+            error_log('lp_controller: action: content');
+            error_log('Item id is '.intval($_GET['item_id']));
         }
         if (!$lp_found) {
             require 'lp_list.php';
         } else {
             if ($debug > 0) {
-                error_log('New LP - save_last()', 0);
+                error_log('save_last()');
             }
             $_SESSION['oLP']->save_last();
             if ($debug > 0) {
-                error_log('New LP - set_current_item()', 0);
+                error_log('set_current_item('.$_GET['item_id'].')');
             }
             $_SESSION['oLP']->set_current_item($_GET['item_id']);
             if ($debug > 0) {
-                error_log('New LP - start_current_item()', 0);
+                error_log('start_current_item()');
             }
             $_SESSION['oLP']->start_current_item();
             require 'lp_content.php';
@@ -1178,7 +1176,7 @@ switch ($action) {
             require 'lp_list.php';
         } else {
             if ($debug > 0) {
-                error_log('New LP - Trying to set current item to '.$_REQUEST['item_id'], 0);
+                error_log('Trying to set current item to '.$_REQUEST['item_id'], 0);
             }
             if (!empty($_REQUEST['item_id'])) {
                 $_SESSION['oLP']->set_current_item($_REQUEST['item_id']);
@@ -1269,12 +1267,18 @@ switch ($action) {
             $_SESSION['oLP']->save_current();
             $_SESSION['oLP']->save_last();
             if ($debug > 0) {
-                error_log('New LP - save_current()');
-                error_log('New LP - save_last()');
+                error_log('save_current()');
+                error_log('save_last()');
             }
             $url = api_get_path(WEB_COURSE_PATH).api_get_course_path().'/index.php?id_session='.api_get_session_id();
-            if (isset($_GET['redirectTo']) && $_GET['redirectTo'] == 'lp_list') {
-                $url = 'lp_controller.php?'.api_get_cidreq();
+            $redirectTo = isset($_GET['redirectTo']) ? $_GET['redirectTo'] : '';
+            switch ($redirectTo) {
+                case 'lp_list':
+                    $url = 'lp_controller.php?'.api_get_cidreq();
+                    break;
+                case 'my_courses':
+                    $url = api_get_path(WEB_PATH).'user_portal.php';
+                    break;
             }
             header('location: '.$url);
             exit;
@@ -1291,7 +1295,7 @@ switch ($action) {
             require 'lp_list.php';
         } else {
             if ($debug > 0) {
-                error_log('New LP - Trying to impress this LP item to '.$_REQUEST['item_id'], 0);
+                error_log('Trying to impress this LP item to '.$_REQUEST['item_id'], 0);
             }
             if (!empty($_REQUEST['item_id'])) {
                 $_SESSION['oLP']->set_current_item($_REQUEST['item_id']);
@@ -1458,7 +1462,7 @@ switch ($action) {
 if (!empty($_SESSION['oLP'])) {
     $_SESSION['lpobject'] = serialize($_SESSION['oLP']);
     if ($debug > 0) {
-        error_log('New LP - lpobject is serialized in session', 0);
+        error_log('lpobject is serialized in session', 0);
     }
 }
 

+ 9 - 9
main/lp/lp_list.php

@@ -644,13 +644,13 @@ foreach ($categories as $item) {
                 /* Export */
                 if ($details['lp_type'] == 1) {
                     $dsp_disk = Display::url(
-                        Display::return_icon('cd.png', get_lang('Export')),
+                        Display::return_icon('cd.png', get_lang('ExportShort')),
                         api_get_self()."?".api_get_cidreq()
                             ."&action=export&lp_id=$id"
                     );
                 } elseif ($details['lp_type'] == 2) {
                     $dsp_disk = Display::url(
-                        Display::return_icon('cd.png', get_lang('Export')),
+                        Display::return_icon('cd.png', get_lang('ExportShort')),
                         api_get_self()."?".api_get_cidreq()
                             ."&action=export&lp_id=$id&export_name="
                             .api_replace_dangerous_char($name).".zip"
@@ -658,7 +658,7 @@ foreach ($categories as $item) {
                 } else {
                     $dsp_disk = Display::return_icon(
                         'cd_na.png',
-                        get_lang('Export')
+                        get_lang('ExportShort')
                     );
                 }
 
@@ -886,6 +886,12 @@ foreach ($categories as $item) {
     ];
 }
 
+// Deleting the objects
+Session::erase('oLP');
+Session::erase('lpobject');
+learnpath::generate_learning_path_folder($courseInfo);
+DocumentManager::removeGeneratedAudioTempFile();
+
 $template = new Template($nameTools);
 $template->assign('subscription_settings', $subscriptionSettings);
 $template->assign('is_allowed_to_edit', $is_allowed_to_edit);
@@ -901,9 +907,3 @@ $templateName = $template->get_template('learnpath/list.tpl');
 $content = $template->fetch($templateName);
 $template->assign('content', $content);
 $template->display_one_col_template();
-learnpath::generate_learning_path_folder($courseInfo);
-
-// Deleting the objects
-Session::erase('oLP');
-Session::erase('lpobject');
-DocumentManager::removeGeneratedAudioTempFile();

+ 6 - 9
main/lp/lp_nav.php

@@ -18,7 +18,8 @@ $htmlHeadXtra[] = '<script>
     var chamilo_xajax_handler = window.parent.oxajax;
 </script>';
 
-$lpItemId = isset($_REQUEST['lp_item']) ? intval($_REQUEST['lp_item']) : 0;
+$lpItemId = isset($_REQUEST['lp_item']) ? (int) $_REQUEST['lp_item'] : 0;
+$lpId = isset($_REQUEST['lp_id']) ? (int) $_REQUEST['lp_id'] : 0;
 
 if (!$lpItemId) {
     echo '';
@@ -27,29 +28,25 @@ if (!$lpItemId) {
 
 $progress_bar = '';
 $navigation_bar = '';
-$display_mode = '';
 $autostart = 'true';
 
-$myLP = learnpath::getLpFromSession(api_get_course_id(), '', '');
+$myLP = learnpath::getLpFromSession(api_get_course_id(), $lpId, api_get_user_id());
 
 if ($myLP) {
-    $display_mode = $myLP->mode;
-    $scorm_css_header = true;
     $lp_theme_css = $myLP->get_theme();
     $my_style = api_get_visual_theme();
 
     // Setting up the CSS theme if exists
-    $mycourselptheme = null;
+    $myCourseLpTheme = null;
     if (api_get_setting('allow_course_theme') === 'true') {
-        $mycourselptheme = api_get_course_setting('allow_learning_path_theme');
+        $myCourseLpTheme = api_get_course_setting('allow_learning_path_theme');
     }
 
-    if (!empty($lp_theme_css) && !empty($mycourselptheme) && $mycourselptheme != -1 && $mycourselptheme == 1) {
+    if (!empty($lp_theme_css) && !empty($myCourseLpTheme) && $myCourseLpTheme != -1 && $myCourseLpTheme == 1) {
         global $lp_theme_css;
     } else {
         $lp_theme_css = $my_style;
     }
-
     $progress_bar = $myLP->getProgressBar();
     $navigation_bar = $myLP->get_navigation_bar();
     $mediaplayer = $myLP->get_mediaplayer($lpItemId, $autostart);

+ 0 - 2
main/lp/lp_subscribe_users_to_category.php

@@ -119,7 +119,6 @@ foreach ($subscribedUsers as $user) {
 
 // Getting subscribed users to a category.
 $subscribedUsersInCategory = $category->getUsers();
-
 $selectedChoices = [];
 foreach ($subscribedUsersInCategory as $item) {
     $selectedChoices[] = $item->getUser()->getId();
@@ -138,7 +137,6 @@ $userMultiSelect = $formUsers->addElement(
 $formUsers->addButtonSave(get_lang('Save'));
 
 $defaults = [];
-
 if (!empty($selectedChoices)) {
     $defaults['users'] = $selectedChoices;
 }

+ 79 - 0
main/lp/lp_update_scorm.php

@@ -0,0 +1,79 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+/**
+ * This is a learning path creation and player tool in Chamilo - previously.
+ *
+ * @author Julio Montoya  - Improving the list of templates
+ *
+ * @package chamilo.learnpath
+ */
+$this_section = SECTION_COURSES;
+
+require_once __DIR__.'/../inc/global.inc.php';
+
+api_protect_course_script();
+$allow = api_is_allowed_to_edit(null, true);
+$lpId = !empty($_GET['lp_id']) ? intval($_GET['lp_id']) : 0;
+
+if (!$allow || empty($lpId)) {
+    api_not_allowed(true);
+}
+
+$lp = new learnpath(api_get_course_id(), $lpId, api_get_user_id());
+
+if (api_is_in_gradebook()) {
+    $interbreadcrumb[] = [
+        'url' => Category::getUrl(),
+        'name' => get_lang('ToolGradebook'),
+    ];
+}
+
+$interbreadcrumb[] = [
+    'url' => 'lp_controller.php?action=list&'.api_get_cidreq(),
+    'name' => get_lang('LearningPaths'),
+];
+$interbreadcrumb[] = [
+    'url' => api_get_self()."?action=build&lp_id=$lpId&".api_get_cidreq(),
+    'name' => $lp->get_name(),
+];
+
+$form = new FormValidator(
+    '',
+    'POST',
+    api_get_self().'?'.api_get_cidreq().'&lp_id='.$lpId,
+    '',
+    [
+        'id' => "upload_form",
+        'enctype' => "multipart/form-data",
+    ]
+);
+$form->addHeader(get_lang('UpdateFile'));
+$form->addHtml(Display::return_message(get_lang('TheScormPackageWillBeUpdatedYouMustUploadTheFileWithTheSameName')));
+$form->addLabel(null, Display::return_icon('scorm_logo.jpg', null, ['style' => 'width:230px;height:100px']));
+$form->addElement('hidden', 'curdirpath', '');
+$form->addElement('file', 'user_file', get_lang('FileToUpload'));
+$form->addRule('user_file', get_lang('ThisFieldIsRequired'), 'required');
+$form->addButtonUpload(get_lang('Upload'));
+
+if ($form->validate()) {
+    $oScorm = new scorm();
+    $manifest = $oScorm->import_package(
+        $_FILES['user_file'],
+        '',
+        api_get_course_info(),
+        true,
+        $lp
+    );
+    if ($manifest) {
+        Display::addFlash(Display::return_message(get_lang('Updated')));
+    }
+    header('Location: '.api_get_path(WEB_CODE_PATH).'lp/lp_list.php?'.api_get_cidreq());
+    exit;
+}
+
+$content = $form->returnForm();
+
+$tpl = new Template(null);
+$tpl->assign('content', $content);
+$tpl->display_one_col_template();

+ 14 - 11
main/lp/lp_view.php

@@ -276,16 +276,13 @@ if (!empty($_REQUEST['exeId']) &&
     if ($safe_id == strval(intval($safe_id)) &&
         $safe_item_id == strval(intval($safe_item_id))
     ) {
-        $sql = 'SELECT start_date, exe_date, exe_result, exe_weighting, exe_exo_id
+        $sql = 'SELECT start_date, exe_date, exe_result, exe_weighting, exe_exo_id, exe_duration
                 FROM '.$TBL_TRACK_EXERCICES.'
                 WHERE exe_id = '.$safe_exe_id;
         $res = Database::query($sql);
         $row_dates = Database::fetch_array($res);
 
-        $time_start_date = api_strtotime($row_dates['start_date'], 'UTC');
-        $time_exe_date = api_strtotime($row_dates['exe_date'], 'UTC');
-
-        $mytime = (int) $time_exe_date - (int) $time_start_date;
+        $duration = (int) $row_dates['exe_duration'];
         $score = (float) $row_dates['exe_result'];
         $max_score = (float) $row_dates['exe_weighting'];
 
@@ -326,7 +323,7 @@ if (!empty($_REQUEST['exeId']) &&
             $sql = "UPDATE $TBL_LP_ITEM_VIEW SET
                         status = '$status',
                         score = $score,
-                        total_time = $mytime
+                        total_time = $duration
                     WHERE iid = $lp_item_view_id";
             if ($debug) {
                 error_log($sql);
@@ -438,10 +435,16 @@ if ($is_allowed_to_edit) {
 }
 
 $buttonHomeText = get_lang('CourseHomepageLink');
-// Return to lp list
-if (api_get_course_setting('lp_return_link') == 1) {
-    $buttonHomeUrl .= '&redirectTo=lp_list';
-    $buttonHomeText = get_lang('LearningPathList');
+$returnLink = api_get_course_setting('lp_return_link');
+switch ($returnLink) {
+    case 1: // lp list
+        $buttonHomeUrl .= '&redirectTo=lp_list';
+        $buttonHomeText = get_lang('LearningPathList');
+        break;
+    case 2: // user portal
+        $buttonHomeUrl .= '&redirectTo=my_courses';
+        $buttonHomeText = get_lang('MyCourses');
+        break;
 }
 
 $lpPreviewImagePath = Display::returnIconPath('unknown.png', ICON_SIZE_BIG);
@@ -542,7 +545,7 @@ $template->display_no_layout_template();
 // Restore a global setting.
 $_setting['show_navigation_menu'] = $save_setting;
 
-Session::write('lp', $lp);
+Session::write('oLP', $lp);
 
 if ($debug) {
     error_log(' ------- end lp_view.php ------');

+ 0 - 1
main/lp/openoffice_text_document.class.php

@@ -211,7 +211,6 @@ class OpenOfficeTextDocument extends OpenofficeDocument
         $_course = api_get_course_info();
         // Split document to pages.
         $pages = explode('||page_break||', $body);
-        $first_item = 0;
         foreach ($pages as $key => $page_content) {
             // For every pages, we create a new file.
             $key += 1;

+ 1 - 1
main/lp/scorm_api.php

@@ -1666,7 +1666,7 @@ function switch_item(current_item, next_item){
     // (4) refresh the audio player if needed
     $.ajax({
         type: "POST",
-        url: "lp_nav.php"+courseUrl,
+        url: "lp_nav.php"+courseUrl+ "&lp_id=" + olms.lms_lp_id,
         data: {
             lp_item: next_item
         },

+ 6 - 1
main/mySpace/exercise_category_report.php

@@ -54,7 +54,12 @@ if (empty($courseId)) {
     $courseInfo = api_get_course_info_by_id($courseId);
     if (!empty($courseInfo)) {
         $form->addHidden('course_id', $courseId);
-        $form->addLabel(get_lang('Course'), $courseInfo['name'].' ('.$courseInfo['code'].')');
+        $courseLabel = Display::url(
+            $courseInfo['name'].' ('.$courseInfo['code'].')',
+            $courseInfo['course_public_url'],
+            ['target' => '_blank']
+        );
+        $form->addLabel(get_lang('Course'), $courseLabel);
         $exerciseList = ExerciseLib::get_all_exercises_for_course_id(
             $courseInfo,
             0,

+ 23 - 6
main/mySpace/myStudents.php

@@ -220,6 +220,21 @@ switch ($action) {
             exit;
         }
         break;
+    case 'generate_certificate':
+        // Delete old certificate
+        $myCertificate = GradebookUtils::get_certificate_by_user_id(
+            0,
+            $student_id
+        );
+        if ($myCertificate) {
+            $certificate = new Certificate($myCertificate['id'], $student_id);
+            $certificate->delete(true);
+        }
+        // Create new one
+        $certificate = new Certificate(0, $student_id);
+        $certificate->generatePdfFromCustomCertificate();
+        exit;
+        break;
     case 'send_legal':
         $subject = get_lang('SendLegalSubject');
         $content = sprintf(
@@ -878,13 +893,13 @@ if (empty($details)) {
                     $progress = Tracking::get_avg_student_progress(
                         $user_info['user_id'],
                         $courseCodeItem,
-                        null,
+                        [],
                         $sId
                     );
                     $score = Tracking:: get_avg_student_score(
                         $user_info['user_id'],
                         $courseCodeItem,
-                        null,
+                        [],
                         $sId
                     );
                     $progress = empty($progress) ? '0%' : $progress.'%';
@@ -1479,7 +1494,8 @@ if (empty($details)) {
             echo '<tr>';
             echo '<td>'.$work->title.'</td>';
             $documentNumber = $key + 1;
-            echo '<td class="text-center"><a href="'.api_get_path(WEB_CODE_PATH).'work/view.php?cidReq='.$course_code.'&id_session='.$sessionId.'&id='.$results['id'].'">('.$documentNumber.')</a></td>';
+            $url = api_get_path(WEB_CODE_PATH).'work/view.php?cidReq='.$course_code.'&id_session='.$sessionId.'&id='.$results['id'];
+            echo '<td class="text-center"><a href="'.$url.'">('.$documentNumber.')</a></td>';
             $qualification = !empty($results['qualification']) ? $results['qualification'] : '-';
             echo '<td class="text-center">'.$qualification.'</td>';
             echo '<td class="text-center">'.$results['formatted_date'].'</td>';
@@ -1503,7 +1519,6 @@ if (empty($details)) {
                     echo '<td class="text-center">'.$field->getValue().'</td>';
                 }
             }
-
             echo '</tr>';
         }
     }
@@ -1588,10 +1603,12 @@ if (empty($details)) {
     </div>
 <?php
 } //end details
+
 echo Tracking::displayUserSkills(
     $user_info['user_id'],
     $courseInfo ? $courseInfo['real_id'] : 0,
-    $sessionId
+    $sessionId,
+    api_get_configuration_value('allow_teacher_access_student_skills')
 );
 
 if ($allowMessages === true) {
@@ -1643,7 +1660,7 @@ if ($allowMessages === true) {
     $form = new FormValidator(
         'messages',
         'post',
-        api_get_self().'?action=send_message&student='.$student_id
+        $currentUrl.'&action=send_message'
     );
     $form->addHtml('<div id="compose_message" style="display:none;">');
     $form->addText('subject', get_lang('Subject'));

+ 18 - 20
main/notebook/index.php

@@ -107,16 +107,15 @@ if ($action === 'addnote') {
         Security::clear_token();
         NotebookManager::display_notes();
     } else {
-        echo '<div class="actions">';
-        echo '<a href="index.php">'.
-            Display::return_icon(
-                'back.png',
-                get_lang('BackToNotesList'),
-                '',
-                ICON_SIZE_MEDIUM
-            ).
-            '</a>';
-        echo '</div>';
+        echo Display::toolbarAction(
+            'add_glossary',
+            [
+                Display::url(
+                    Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM),
+                    api_get_self().'?'.api_get_cidreq()
+                ),
+            ]
+        );
         $token = Security::get_token();
         $form->addElement('hidden', 'sec_token');
         $form->setConstants(['sec_token' => $token]);
@@ -171,16 +170,15 @@ if ($action === 'addnote') {
         Security::clear_token();
         NotebookManager::display_notes();
     } else {
-        echo '<div class="actions">';
-        echo '<a href="index.php">'.
-            Display::return_icon(
-                'back.png',
-                get_lang('BackToNotesList'),
-                '',
-                ICON_SIZE_MEDIUM
-            ).
-            '</a>';
-        echo '</div>';
+        echo Display::toolbarAction(
+            'add_glossary',
+            [
+                Display::url(
+                    Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM),
+                    api_get_self().'?'.api_get_cidreq()
+                ),
+            ]
+        );
         $token = Security::get_token();
         $form->addElement('hidden', 'sec_token');
         $form->setConstants(['sec_token' => $token]);

+ 22 - 13
main/session/resume_session.php

@@ -1,6 +1,7 @@
 <?php
 /* For licensing terms, see /license.txt */
 
+use Chamilo\CoreBundle\Entity\Course;
 use Chamilo\CoreBundle\Entity\Promotion;
 use Chamilo\CoreBundle\Entity\Repository\SequenceRepository;
 use Chamilo\CoreBundle\Entity\Repository\SessionRepository;
@@ -158,6 +159,9 @@ if ($sessionInfo['nbr_courses'] == 0) {
     $courseItem = '';
     $courses = $sessionRepository->getCoursesOrderedByPosition($session);
 
+    $allowSkills = api_get_configuration_value('allow_skill_rel_items');
+
+    /** @var Course $course */
     foreach ($courses as $course) {
         // Select the number of users
         $numberOfUsers = SessionManager::getCountUsersInCourseSession(
@@ -166,8 +170,7 @@ if ($sessionInfo['nbr_courses'] == 0) {
         );
         // Get coachs of the courses in session
         $namesOfCoaches = [];
-        $coachSubscriptions = $session
-            ->getUserCourseSubscriptionsByStatus($course, Session::COACH)
+        $coachSubscriptions = $session->getUserCourseSubscriptionsByStatus($course, Session::COACH)
             ->forAll(function ($index, SessionRelCourseRelUser $subscription) use (&$namesOfCoaches) {
                 $namesOfCoaches[] = $subscription->getUser()->getCompleteNameWithUserName();
 
@@ -209,14 +212,20 @@ if ($sessionInfo['nbr_courses'] == 0) {
             ).'</td>';
         $courseItem .= '<td>'.($namesOfCoaches ? implode('<br>', $namesOfCoaches) : get_lang('None')).'</td>';
         $courseItem .= '<td>'.$numberOfUsers.'</td>';
-        $courseItem .= '
-			<td>
-                <a href="'.$courseUrl.'">'.
-                Display::return_icon('course_home.gif', get_lang('Course')).'</a>
-                '.$orderButtons.'
-                <a href="session_course_user_list.php?id_session='.$sessionId.'&course_code='.$course->getCode().'">'.
-                Display::return_icon('user.png', get_lang('Users'), '', ICON_SIZE_SMALL).'</a>
-                <a href="'.api_get_path(WEB_CODE_PATH).'user/user_import.php?action=import&cidReq='.$course->getCode().'&id_session='.$sessionId.'">'.
+        $courseItem .= '<td>';
+        $courseItem .= Display::url(Display::return_icon('course_home.gif', get_lang('Course')), $courseUrl);
+
+        if ($allowSkills) {
+            $courseItem .= Display::url(
+                Display::return_icon('skills.png', get_lang('Skills')),
+                api_get_path(WEB_CODE_PATH).'admin/skill_rel_course.php?session_id='.$sessionId.'&course_id='.$course->getId()
+            );
+        }
+
+        $courseItem .= $orderButtons;
+        $courseItem .= '<a href="session_course_user_list.php?id_session='.$sessionId.'&course_code='.$course->getCode().'">'.
+                Display::return_icon('user.png', get_lang('Users'), '', ICON_SIZE_SMALL).'</a>';
+        $courseItem .= '<a href="'.api_get_path(WEB_CODE_PATH).'user/user_import.php?action=import&cidReq='.$course->getCode().'&id_session='.$sessionId.'">'.
                 Display::return_icon('import_csv.png', get_lang('ImportUsersToACourse'), null, ICON_SIZE_SMALL).'</a>
                 <a href="'.api_get_path(WEB_CODE_PATH).'user/user_export.php?file_type=csv&course_session='.$course->getCode().':'.$sessionId.'&addcsvheader=1">'.
                 Display::return_icon('export_csv.png', get_lang('ExportUsersOfACourse'), null, ICON_SIZE_SMALL).'</a>
@@ -225,9 +234,9 @@ if ($sessionInfo['nbr_courses'] == 0) {
 				<a href="session_course_edit.php?id_session='.$sessionId.'&page=resume_session.php&course_code='.$course->getCode().''.$orig_param.'">'.
                 Display::return_icon('teacher.png', get_lang('ModifyCoach'), '', ICON_SIZE_SMALL).'</a>
 				<a href="'.api_get_self().'?id_session='.$sessionId.'&action=delete&idChecked[]='.$course->getCode().'" onclick="javascript:if(!confirm(\''.get_lang('ConfirmYourChoice').'\')) return false;">'.
-            Display::return_icon('delete.png', get_lang('Delete')).'</a>
-			</td>
-		</tr>';
+            Display::return_icon('delete.png', get_lang('Delete')).'</a>';
+
+        $courseItem .= '</td></tr>';
         $count++;
     }
     $courseListToShow .= $courseItem;

+ 13 - 3
main/session/scheduled_announcement.php

@@ -100,6 +100,10 @@ switch ($action) {
             $res = $object->save($values);
 
             if ($res) {
+                $extraFieldValue = new ExtraFieldValue('scheduled_announcement');
+                $values['item_id'] = $res;
+                $extraFieldValue->saveFieldValues($values);
+
                 Display::addFlash(
                     Display::return_message(
                         get_lang('ItemAdded'),
@@ -122,8 +126,8 @@ switch ($action) {
         break;
     case 'edit':
         // Action handling: Editing
-        $url = api_get_self().'?action='.Security::remove_XSS($_GET['action']).'&id='.intval($_GET['id']).'&session_id='.$sessionId;
-        $form = $object->returnSimpleForm($url, 'edit', $sessionInfo);
+        $url = api_get_self().'?action='.Security::remove_XSS($_GET['action']).'&id='.$id.'&session_id='.$sessionId;
+        $form = $object->returnSimpleForm($id, $url, 'edit', $sessionInfo);
         if ($form->validate()) {
             $values = $form->getSubmitValues();
             $values['id'] = $id;
@@ -131,10 +135,16 @@ switch ($action) {
             $values['date'] = api_get_utc_datetime($values['date']);
             $res = $object->update($values);
 
+            $extraFieldValue = new ExtraFieldValue('scheduled_announcement');
+            $values['item_id'] = $id;
+            $extraFieldValue->saveFieldValues($values);
+
             Display::addFlash(Display::return_message(
                 get_lang('Updated'),
                 'confirmation'
             ));
+            header("Location: $url");
+            exit;
         }
         $item = $object->get($id);
         $item['date'] = api_get_local_time($item['date']);
@@ -142,7 +152,7 @@ switch ($action) {
         $content = $form->returnForm();
         break;
     case 'delete':
-        $object->delete($_GET['id']);
+        $object->delete($id);
         $content = $object->getGrid($sessionId);
         break;
     default:

+ 90 - 15
main/session/session_add.php

@@ -84,7 +84,6 @@ $xajax->processRequests();
 $htmlHeadXtra[] = $xajax->getJavascript('../inc/lib/xajax/');
 $htmlHeadXtra[] = "
 <script>
-
 $(document).ready( function() {
     accessSwitcher(0);
 });
@@ -102,11 +101,10 @@ function accessSwitcher(accessFromReady) {
     }
 
     if (access == 1) {
-        $('#duration').hide();
+        $('#duration_div').hide();
         $('#date_fields').show();
     } else {
-
-        $('#duration').show();
+        $('#duration_div').show();
         $('#date_fields').hide();
     }
     emptyDuration();
@@ -139,13 +137,14 @@ $form->addElement('header', $tool_name);
 $result = SessionManager::setForm($form);
 
 $url = api_get_path(WEB_AJAX_PATH).'session.ajax.php';
+$urlUpload = api_get_path(WEB_UPLOAD_PATH);
+$sysUploadPath = api_get_path(SYS_UPLOAD_PATH);
 $urlAjaxExtraField = api_get_path(WEB_AJAX_PATH).'extra_field.ajax.php?1=1';
 
 $htmlHeadXtra[] = "
 <script>
 $(function() {
     ".$result['js']."
-
     $('#system_template').on('change', function() {
         var sessionId = $(this).find('option:selected').val();
 
@@ -168,13 +167,48 @@ $(function() {
                 if (data.duration > 0) {
                     $('#access').val(0);
                     $('#access').selectpicker('render');
-                    $('#duration').val(data.duration);
+                    accessSwitcher(0);
+                    $('#duration').val(parseInt(data.duration));                    
+                } else {                    
+                    $('#access').val(1);
+                    $('#access').selectpicker('render');
+                    accessSwitcher(1);
+                    
+                    var variables = [
+                        'display_start_date',
+                        'access_start_date',
+                        'coach_access_start_date',
+                        'display_end_date',
+                        'access_end_date',
+                        'coach_access_end_date'                        
+                    ];                    
+                    variables.forEach(function(variable) {
+                        var variableName = variable + '_to_local_time';                        
+                        if (data[variableName]) {                        
+                            var parsedDate = $.datepicker.parseDateTime(
+                                'yy-mm-dd', 
+                                'hh:mm:ss', 
+                                data[variableName]
+                            );         
+                            if (parsedDate) {
+                                $('#'+variable).datetimepicker('setDate', parsedDate);
+                            }           
+                        }
+                    });
+                }
+                
+                $('[name=\'show_description\']').prop('checked', false);
+                if (data.show_description) {
+                    $('[name=\'show_description\']').prop('checked', true);
                 }
+                
+                $('[name=\'send_subscription_notification\']').prop('checked', false);
+                if (data.send_subscription_notification) {
+                    $('[name=\'send_subscription_notification\']').prop('checked', true);
+                } 
 
                 $.each(data.extra_fields, function(i, item) {
-                    var fieldName = 'extra_'+item.variable; 
-                                
-
+                    var fieldName = 'extra_'+item.variable;
                     /*
                     const FIELD_TYPE_TEXT = 1;
                     const FIELD_TYPE_TEXTAREA = 2;
@@ -218,7 +252,8 @@ $(function() {
                             break;
                         case '4': // simple select
                         case '5': // multiple select
-                            $('#'+fieldName+'').val(item.value);
+                            var options = item.value.split(';');                            
+                            $('#'+fieldName+'').val(options);
                             $('#'+fieldName+'').selectpicker('render');
                             break;
                         case '8': // double
@@ -227,10 +262,8 @@ $(function() {
                             // item.value has format : 85::86
                             if (item.value) {
                                 var values = item.value.split('::');
-
                                 var firstFieldId = values[0];
                                 var secondFieldId = values[1];
-
                                 $('#'+first+'').val(firstFieldId);
                                 $('#'+first+'').selectpicker('render');
 
@@ -262,7 +295,6 @@ $(function() {
                             }
                             break;
                         case '10': // tags
-
                              // Remove all options
                             $('#'+fieldName+' option').each(function(i, optionItem) {
                                 $(this).remove();
@@ -294,6 +326,27 @@ $(function() {
                                $('[name=\''+check+'\']').prop('checked', true);
                             }
                             break;
+                        case '16':
+                            if (item.value) {
+                                //    $('input[name='+fieldName+']').val(item.value);
+                                var url = '".$urlUpload."';
+                                
+                                url = url + item.value;
+                                
+                                var divFormGroup = fieldName + '-form-group';
+                                var divWrapper = fieldName + '_crop_image';
+                                var divPreview = fieldName + '_preview_image';
+                                var divCropButton = fieldName + '_crop_button';
+                                var cropResult = fieldName + '_crop_result';
+                                                                
+                                $('[name=\''+cropResult+'\']').val('import_file_from_session::' + sessionId);
+                                $('#' + divFormGroup).show();
+                                $('#' + divWrapper).show();
+                                $('#' + divCropButton).hide();
+                                $('#' + divPreview).attr('src', url);                                
+                                //$('[name=\''+fieldName+'\']')
+                            }
+                            break;
                     }
                 });
             }
@@ -315,7 +368,6 @@ $form->setDefaults($formDefaults);
 
 if ($form->validate()) {
     $params = $form->getSubmitValues();
-
     $name = $params['name'];
     $startDate = $params['access_start_date'];
     $endDate = $params['access_end_date'];
@@ -342,10 +394,33 @@ if ($form->validate()) {
         }
     }
 
-    if (isset($extraFields['extra_image']) && $isThisImageCropped) {
+    if (isset($extraFields['extra_image']) && !empty($extraFields['extra_image']['name']) && $isThisImageCropped) {
         $extraFields['extra_image']['crop_parameters'] = $params['picture_crop_result'];
     }
 
+    // Check if the session image will be copied from the template
+    $importImageFromSession = false;
+    $sessionIdToImport = explode('::', $params['extra_image_crop_result']);
+    $sessionIdToImport = isset($sessionIdToImport[1]) ? (int) $sessionIdToImport[1] : 0;
+    if (!empty($sessionIdToImport)) {
+        $extraField = new ExtraField('session');
+        $extraFieldInfo = $extraField->get_handler_field_info_by_field_variable('image');
+
+        $extraFieldValue = new ExtraFieldValue('session');
+        $extraFieldValueData = $extraFieldValue->get_values_by_handler_and_field_id(
+            $sessionIdToImport,
+            $extraFieldInfo['id']
+        );
+
+        if ($extraFieldValueData && file_exists($sysUploadPath.$extraFieldValueData['value'])) {
+            $extraFields['extra_image']['name'] = basename($extraFieldValueData['value']);
+            $extraFields['extra_image']['tmp_name'] = $sysUploadPath.$extraFieldValueData['value'];
+            $extraFields['extra_image']['type'] = 'image/png';
+            $extraFields['extra_image']['error'] = 0;
+            $extraFields['extra_image']['size'] = filesize($sysUploadPath.$extraFieldValueData['value']);
+        }
+    }
+
     $return = SessionManager::create_session(
         $name,
         $startDate,

+ 2 - 2
main/session/session_list.php

@@ -13,7 +13,7 @@ $this_section = SECTION_PLATFORM_ADMIN;
 
 SessionManager::protectSession(null, false);
 
-// Add the JS needed to use the jqgrid
+//Add the JS needed to use the jqgrid
 $htmlHeadXtra[] = api_get_jqgrid_js();
 
 $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : null;
@@ -42,7 +42,7 @@ if ($action == 'delete') {
 $tool_name = get_lang('SessionList');
 Display::display_header($tool_name);
 
-$courseId = isset($_GET['course_id']) ? $_GET['course_id'] : 0;
+$courseId = isset($_GET['course_id']) ? $_GET['course_id'] : null;
 
 $sessionFilter = new FormValidator(
     'course_filter',

+ 2 - 1
main/social/home.php

@@ -120,7 +120,8 @@ if (!empty($results)) {
         );
 
         $result['picture'] = '<img class="img-responsive" src="'.$picture['file'].'" />';
-        $group_actions = '<div class="group-more"><a class="btn btn-default" href="groups.php?#tab_browse-2">'.get_lang('SeeMore').'</a></div>';
+        $group_actions = '<div class="group-more"><a class="btn btn-default" href="groups.php?#tab_browse-2">'.
+            get_lang('SeeMore').'</a></div>';
         $group_info = '<div class="description"><p>'.cut($result['description'], 120, true)."</p></div>";
         $groups_newest[] = [
             Display::url(

+ 1 - 1
main/social/my_skills_report.php

@@ -25,7 +25,7 @@ if (!$isStudent && !$isStudentBoss && !$isDRH) {
 $action = isset($_GET['a']) ? $_GET['a'] : '';
 switch ($action) {
     case 'generate_custom_skill':
-        $certificate = new Certificate(0, api_get_user_id());
+        $certificate = new Certificate(0, api_get_user_id(), false, false);
         $certificate->generatePdfFromCustomCertificate();
         break;
 }

+ 3 - 2
main/social/search.php

@@ -85,8 +85,9 @@ if ($query != '' || ($query_vars['search_type'] == '1' && count($query_vars) > 2
         $buttonClass = 'btn btn-default btn-sm';
         foreach ($users as $user) {
             $user_info = api_get_user_info($user['id'], true);
-            $sendInvitation = '<button class="'.$buttonClass.' disabled "><em class="fa fa-user"></em> '.get_lang('SendInvitation').'</button>';
-            $relation_type = intval(SocialManager::get_relation_between_contacts(api_get_user_id(), $user_info['user_id']));
+            $sendInvitation = '<button class="'.$buttonClass.' disabled ">
+                <em class="fa fa-user"></em> '.get_lang('SendInvitation').'</button>';
+            $relation_type = SocialManager::get_relation_between_contacts(api_get_user_id(), $user_info['user_id']);
             $url = api_get_path(WEB_PATH).'main/social/profile.php?u='.$user_info['user_id'];
 
             // Show send invitation icon if they are not friends yet

+ 8 - 6
main/survey/create_new_survey.php

@@ -82,9 +82,13 @@ if ($_GET['action'] == 'edit' && isset($survey_id) && is_numeric($survey_id)) {
 
     if ($link_info) {
         $defaults['category_id'] = $link_info['category_id'];
-        if ($sql_result_array = Database::fetch_array(Database::query('SELECT weight FROM '.$table_gradebook_link.' WHERE id='.$gradebook_link_id))) {
+        $gradebook_link_id = (int) $gradebook_link_id;
+        $sql = "SELECT weight FROM $table_gradebook_link WHERE id = $gradebook_link_id";
+        $result = Database::query($sql);
+        $gradeBookData = Database::fetch_array($result);
+        if ($gradeBookData) {
             $defaults['survey_qualify_gradebook'] = $gradebook_link_id;
-            $defaults['survey_weight'] = number_format($sql_result_array['weight'], 2, '.', '');
+            $defaults['survey_weight'] = number_format($gradeBookData['weight'], 2, '.', '');
         }
     }
 } else {
@@ -119,7 +123,7 @@ $survey_code = $form->addElement(
 );
 
 if ($_GET['action'] == 'edit') {
-    //$survey_code->freeze();
+    $survey_code->freeze();
     $form->applyFilter('survey_code', 'api_strtoupper');
 }
 
@@ -326,9 +330,7 @@ if ($form->validate()) {
 } else {
     // Displaying the header
     Display::display_header($tool_name);
-
     $form->display();
 }
 
-// Footer
-Display :: display_footer();
+Display::display_footer();

+ 8 - 4
main/survey/fillsurvey.php

@@ -7,11 +7,15 @@ use ChamiloSession as Session;
  * @package chamilo.survey
  *
  * @author unknown, the initial survey that did not make it in 1.8 because of bad code
- * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University: cleanup, refactoring and rewriting large parts of the code
- * @author Julio Montoya <gugli100@gmail.com>, Chamilo: Personality Test modification and rewriting large parts of the code as well
+ * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University: cleanup,
+ * refactoring and rewriting large parts of the code
+ * @author Julio Montoya <gugli100@gmail.com>, Chamilo: Personality Test
+ * modification and rewriting large parts of the code as well
  *
- * @todo check if the user already filled the survey and if this is the case then the answers have to be updated and not stored again.
- * @todo performance could be improved if not the survey_id was stored with the invitation but the survey_code
+ * @todo check if the user already filled the survey and if this
+ * is the case then the answers have to be updated and not stored again.
+ * @todo performance could be improved if not the survey_id was
+ * stored with the invitation but the survey_code
  */
 
 // Unsetting the course id (because it is in the URL)

+ 59 - 0
main/survey/pending.php

@@ -0,0 +1,59 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+use Chamilo\CoreBundle\Entity\Course;
+use Chamilo\CoreBundle\Entity\Session;
+use Chamilo\CourseBundle\Entity\CSurvey;
+use Chamilo\CourseBundle\Entity\CSurveyInvitation;
+
+$cidReset = true;
+
+require_once __DIR__.'/../inc/global.inc.php';
+
+api_block_anonymous_users(true);
+
+$em = Database::getManager();
+
+$currentUser = api_get_user_entity(api_get_user_id());
+$avatarPath = UserManager::getUserPicture($currentUser->getId());
+$pending = SurveyUtil::getUserPendingInvitations($currentUser->getId());
+
+$surveysData = [];
+
+foreach ($pending as $i => $item) {
+    if (is_a($item, 'Chamilo\CourseBundle\Entity\CSurveyInvitation')) {
+        continue;
+    }
+
+    /** @var CSurvey $survey */
+    $survey = $item;
+    /** @var CSurveyInvitation invitation */
+    $invitation = $pending[$i + 1];
+    /** @var Course $course */
+    $course = $em->find('ChamiloCoreBundle:Course', $survey->getCId());
+    /** @var Session $session */
+    $session = $em->find('ChamiloCoreBundle:Session', $survey->getSessionId());
+
+    $course = $course ? ['id' => $course->getId(), 'title' => $course->getTitle(), 'code' => $course->getCode()] : null;
+    $session = $session ? ['id' => $session->getId(), 'name' => $session->getName()] : null;
+    $surveysData[$survey->getSurveyId()] = [
+        'title' => $survey->getTitle(),
+        'invitation_code' => $invitation->getInvitationCode(),
+        'avail_from' => $survey->getAvailFrom(),
+        'avail_till' => $survey->getAvailTill(),
+        'course' => $course,
+        'session' => $session,
+    ];
+}
+
+$toolName = get_lang('PendingSurveys');
+
+$template = new Template($toolName);
+$template->assign('user', $currentUser);
+$template->assign('user_avatar', $avatarPath);
+$template->assign('surveys', $surveysData);
+$layout = $template->get_template('survey/pending.tpl');
+$content = $template->fetch($layout);
+$template->assign('header', $toolName);
+$template->assign('content', $content);
+$template->display_one_col_template();

+ 2 - 3
main/survey/question.php

@@ -5,9 +5,8 @@
  * @package chamilo.survey
  *
  * @author unknown, the initial survey that did not make it in 1.8 because of bad code
- * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University: cleanup, refactoring and rewriting large parts of the code
- *
- * @version $Id: question.php 21734 2009-07-02 17:12:41Z cvargas1 $
+ * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University: cleanup,
+ * refactoring and rewriting large parts of the code
  */
 require_once __DIR__.'/../inc/global.inc.php';
 

+ 2 - 3
main/survey/reporting.php

@@ -5,9 +5,8 @@
  * @package chamilo.survey
  *
  * @author unknown, the initial survey that did not make it in 1.8 because of bad code
- * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University: cleanup, refactoring and rewriting large parts of the code
- *
- * @version $Id: reporting.php 21652 2009-06-27 17:07:35Z herodoto $
+ * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University: cleanup,
+ * refactoring and rewriting large parts of the code
  *
  * @todo The question has to be more clearly indicated (same style as when filling the survey)
  */

+ 2 - 3
main/survey/survey.download.inc.php

@@ -6,8 +6,6 @@
  *
  * @author Arnaud Ligot <arnaud@cblue.be>
  *
- * @version $Id: $
- *
  * A small peace of code to enable user to access images included into survey
  * which are accessible by non authenticated users. This file is included
  * by document/download.php
@@ -44,7 +42,8 @@ function check_download_survey($course, $invitation, $doc_url)
         exit;
     }
 
-    // Very basic security check: check if a text field from a survey/answer/option contains the name of the document requested
+    // Very basic security check: check if a text field from
+    // a survey/answer/option contains the name of the document requested
     // Fetch survey ID
     // If this is the case there will be a language choice
     $sql = "SELECT * FROM $table_survey

+ 19 - 22
main/survey/survey.lib.php

@@ -30,12 +30,12 @@ class SurveyManager
             return false;
         }
         $course_id = api_get_course_int_id();
-        $table_survey = Database::get_course_table(TABLE_SURVEY);
+        $table = Database::get_course_table(TABLE_SURVEY);
         $code = Database::escape_string($code);
         $num = 0;
         $new_code = $code;
         while (true) {
-            $sql = "SELECT * FROM $table_survey
+            $sql = "SELECT * FROM $table
                     WHERE code = '$new_code' AND c_id = $course_id";
             $result = Database::query($sql);
             if (Database::num_rows($result)) {
@@ -92,7 +92,6 @@ class SurveyManager
      */
     public static function get_surveys($course_code, $session_id = 0)
     {
-        $table_survey = Database::get_course_table(TABLE_SURVEY);
         if (empty($course_code)) {
             return false;
         }
@@ -104,7 +103,8 @@ class SurveyManager
 
         $session_condition = api_get_session_condition($session_id, true, true);
 
-        $sql = "SELECT * FROM $table_survey
+        $table = Database::get_course_table(TABLE_SURVEY);
+        $sql = "SELECT * FROM $table
                 WHERE c_id = {$course_info['real_id']} $session_condition ";
         $result = Database::query($sql);
         $result = Database::store_result($result, 'ASSOC');
@@ -251,9 +251,7 @@ class SurveyManager
             }
 
             $values['anonymous'] = intval($values['anonymous']);
-            $additional['columns'] = '';
             $extraParams = [];
-
             if ($values['anonymous'] == 0) {
                 // Input_name_list
                 $values['show_form_profile'] = isset($values['show_form_profile']) ? $values['show_form_profile'] : 0;
@@ -409,9 +407,7 @@ class SurveyManager
             }
 
             $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['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 = [];
@@ -499,7 +495,6 @@ class SurveyManager
         }
 
         $gradebook_link_type = 8;
-
         $link_info = GradebookUtils::isResourceInCourseGradebook(
             $courseCode,
             $gradebook_link_type,
@@ -1012,7 +1007,6 @@ class SurveyManager
         }
 
         // Getting the information of the question options
-
         $result = Database::query($sqlOption);
         while ($row = Database::fetch_array($result, 'ASSOC')) {
             /** @todo this should be renamed to options instead of answers */
@@ -1084,6 +1078,8 @@ class SurveyManager
      * @param array $survey_data
      * @param array $form_content all the information of the form
      *
+     * @return string
+     *
      * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
      *
      * @version January 2007
@@ -1127,7 +1123,6 @@ class SurveyManager
                     $empty_answer = true;
                 }
             }
-            $additional = [];
             $course_id = api_get_course_int_id();
 
             if (!$empty_answer) {
@@ -1393,7 +1388,7 @@ class SurveyManager
      *
      * @param int $survey_id the id of the survey that has to be deleted
      *
-     * @return true
+     * @return bool
      *
      * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
      *
@@ -1422,6 +1417,8 @@ class SurveyManager
 
         // Deleting all the answers on this survey
         self::delete_all_survey_answers($survey_id);
+
+        return true;
     }
 
     /**
@@ -1440,14 +1437,14 @@ class SurveyManager
     public static function delete_survey_question($survey_id, $question_id, $shared = false)
     {
         $course_id = api_get_course_int_id();
-        // Table definitions
-        $table_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
         if ($shared) {
             self::delete_shared_survey_question($survey_id, $question_id);
         }
 
+        // Table definitions
+        $table = Database::get_course_table(TABLE_SURVEY_QUESTION);
         // Deleting the survey questions
-        $sql = "DELETE FROM $table_survey_question
+        $sql = "DELETE FROM $table
 		        WHERE
 		            c_id = $course_id AND
 		            survey_id='".intval($survey_id)."' AND
@@ -1679,9 +1676,9 @@ class SurveyManager
     public static function delete_all_survey_answers($survey_id)
     {
         $course_id = api_get_course_int_id();
-        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
+        $table = Database::get_course_table(TABLE_SURVEY_ANSWER);
         $survey_id = intval($survey_id);
-        $sql = "DELETE FROM $table_survey_answer 
+        $sql = "DELETE FROM $table 
                 WHERE c_id = $course_id AND survey_id=$survey_id";
         Database::query($sql);
 
@@ -1697,13 +1694,13 @@ class SurveyManager
      */
     public static function is_user_filled_survey($user_id, $survey_id, $course_id)
     {
-        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
+        $table = Database::get_course_table(TABLE_SURVEY_ANSWER);
         $user_id = intval($user_id);
         $course_id = intval($course_id);
         $survey_id = intval($survey_id);
 
         $sql = "SELECT DISTINCT user 
-                FROM $table_survey_answer
+                FROM $table
                 WHERE
                     c_id		= $course_id AND
                     user		= $user_id AND
@@ -1962,8 +1959,8 @@ class SurveyManager
      * This function copy survey specifying course id and session id where will be copied.
      *
      * @param int $surveyId
-     * @param int $courseId  target course id
-     * @param int $sessionId target session id
+     * @param int $targetCourseId  target course id
+     * @param int $targetSessionId target session id
      *
      * @return bool|int when fails or return the new survey id
      */

+ 109 - 47
main/survey/surveyUtil.class.php

@@ -2829,10 +2829,14 @@ class SurveyUtil
      * @param int  $survey_id the id of the survey
      * @param bool $drh
      *
-     * @return string html code that are the actions that can be performed on any survey
+     * @throws \Doctrine\ORM\ORMException
+     * @throws \Doctrine\ORM\OptimisticLockException
+     * @throws \Doctrine\ORM\TransactionRequiredException
      *
      * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
      *
+     * @return string html code that are the actions that can be performed on any survey
+     *
      * @version January 2007
      */
     public static function modify_filter($survey_id, $drh = false)
@@ -2850,12 +2854,15 @@ class SurveyUtil
         }
 
         $survey_id = $survey->getSurveyId();
-        $return = '';
+        $actions = [];
         $hideReportingButton = api_get_configuration_value('hide_survey_reporting_button');
+        $codePath = api_get_path(WEB_CODE_PATH);
+        $params = [];
+        parse_str(api_get_cidreq(), $params);
 
         $reportingLink = Display::url(
-            Display::return_icon('statistics.png', get_lang('Reporting'), [], ICON_SIZE_SMALL),
-            api_get_path(WEB_CODE_PATH).'survey/reporting.php?'.api_get_cidreq().'&survey_id='.$survey_id
+            Display::return_icon('statistics.png', get_lang('Reporting')),
+            $codePath.'survey/reporting.php?'.http_build_query($params + ['survey_id' => $survey_id])
         );
 
         if ($drh) {
@@ -2866,50 +2873,62 @@ class SurveyUtil
         if (api_is_allowed_to_edit() ||
             api_is_element_in_the_session(TOOL_SURVEY, $survey_id)
         ) {
-            $return .= '<a href="'.api_get_path(WEB_CODE_PATH).'survey/create_new_survey.php?'.api_get_cidreq()
-                .'&action=edit&survey_id='.$survey_id.'">'
-                .Display::return_icon('edit.png', get_lang('Edit'), '', ICON_SIZE_SMALL)
-                .'</a>';
+            $actions[] = Display::url(
+                Display::return_icon('edit.png', get_lang('Edit')),
+                $codePath.'survey/create_new_survey.php?'
+                    .http_build_query($params + ['action' => 'edit', 'survey_id' => $survey_id])
+            );
             if (SurveyManager::survey_generation_hash_available()) {
-                $return .= Display::url(
-                    Display::return_icon('new_link.png', get_lang('GenerateSurveyAccessLink'), '', ICON_SIZE_SMALL),
-                    api_get_path(WEB_CODE_PATH).'survey/generate_link.php?survey_id='.$survey_id.'&'.api_get_cidreq()
+                $actions[] = Display::url(
+                    Display::return_icon('new_link.png', get_lang('GenerateSurveyAccessLink')),
+                    $codePath.'survey/generate_link.php?'.http_build_query($params + ['survey_id' => $survey_id])
                 );
             }
-            $return .= Display::url(
-                Display::return_icon('backup.png', get_lang('CopySurvey'), '', ICON_SIZE_SMALL),
-                'copy_survey.php?survey_id='.$survey_id.'&'.api_get_cidreq()
+            $actions[] = Display::url(
+                Display::return_icon('backup.png', get_lang('CopySurvey')),
+                $codePath.'survey/copy_survey.php?'.http_build_query($params + ['survey_id' => $survey_id])
             );
-            $return .= Display::url(
-                Display::return_icon('copy.png', get_lang('DuplicateSurvey'), '', ICON_SIZE_SMALL),
-                'survey_list.php?action=copy_survey&survey_id='.$survey_id.'&'.api_get_cidreq()
+            $actions[] = Display::url(
+                Display::return_icon('copy.png', get_lang('DuplicateSurvey')),
+                $codePath.'survey/survey_list.php?'
+                    .http_build_query($params + ['action' => 'copy_survey', 'survey_id' => $survey_id])
             );
 
-            $return .= ' <a href="'.api_get_path(WEB_CODE_PATH).'survey/survey_list.php?'.api_get_cidreq()
-                .'&action=empty&survey_id='.$survey_id.'" onclick="javascript: if(!confirm(\''
-                .addslashes(api_htmlentities(get_lang("EmptySurvey").'?')).'\')) return false;">'
-                .Display::return_icon('clean.png', get_lang('EmptySurvey'), '', ICON_SIZE_SMALL)
-                .'</a>&nbsp;';
+            $warning = addslashes(api_htmlentities(get_lang("EmptySurvey").'?', ENT_QUOTES));
+            $actions[] = Display::url(
+                Display::return_icon('clean.png', get_lang('EmptySurvey')),
+                $codePath.'survey/survey_list.php'
+                    .http_build_query($params + ['action' => 'empty', 'survey_id' => $survey_id]),
+                [
+                    'onclick' => "javascript: if (!confirm('".$warning."')) return false;",
+                ]
+            );
         }
-        $return .= '<a href="'.api_get_path(WEB_CODE_PATH).'survey/preview.php?'.api_get_cidreq().'&survey_id='.$survey_id.'">'
-            .Display::return_icon('preview_view.png', get_lang('Preview'), '', ICON_SIZE_SMALL)
-            .'</a>&nbsp;';
-        $return .= '<a href="'.api_get_path(WEB_CODE_PATH).'survey/survey_invite.php?'.api_get_cidreq().'&survey_id='.$survey_id.'">'
-            .Display::return_icon('mail_send.png', get_lang('Publish'), '', ICON_SIZE_SMALL)
-            .'</a>&nbsp;';
-        $return .= $hideReportingButton ? '' : $reportingLink;
+        $actions[] = Display::url(
+            Display::return_icon('preview_view.png', get_lang('Preview')),
+            $codePath.'survey/preview.php?'.http_build_query($params + ['survey_id' => $survey_id])
+        );
+        $actions[] = Display::url(
+            Display::return_icon('mail_send.png', get_lang('Publish')),
+            $codePath.'survey/survey_invite.php?'.http_build_query($params + ['survey_id' => $survey_id])
+        );
+        $actions[] = $hideReportingButton ? null : $reportingLink;
 
         if (api_is_allowed_to_edit() ||
             api_is_element_in_the_session(TOOL_SURVEY, $survey_id)
         ) {
-            $return .= '<a href="'.api_get_path(WEB_CODE_PATH).'survey/survey_list.php?'.api_get_cidreq()
-                .'&action=delete&survey_id='.$survey_id.'" onclick="javascript: if(!confirm(\''
-                .addslashes(api_htmlentities(get_lang("DeleteSurvey").'?', ENT_QUOTES)).'\')) return false;">'
-                .Display::return_icon('delete.png', get_lang('Delete'), '', ICON_SIZE_SMALL)
-                .'</a>&nbsp;';
+            $warning = addslashes(api_htmlentities(get_lang("DeleteSurvey").'?', ENT_QUOTES));
+            $actions[] = Display::url(
+                Display::return_icon('delete.png', get_lang('Delete')),
+                $codePath.'survey/survey_list.php?'
+                    .http_build_query($params + ['action' => 'delete', 'survey_id' => $survey_id]),
+                [
+                    'onclick' => "javascript: if(!confirm('".$warning."')) return false;",
+                ]
+            );
         }
 
-        return $return;
+        return implode(PHP_EOL, $actions);
     }
 
     /**
@@ -2920,16 +2939,29 @@ class SurveyUtil
     public static function modify_filter_for_coach($survey_id)
     {
         $survey_id = (int) $survey_id;
-        $return = '<a href="'.api_get_path(WEB_CODE_PATH).'survey/preview.php?'.api_get_cidreq().'&survey_id='.$survey_id.'">'
-            .Display::return_icon('preview_view.png', get_lang('Preview'), '', ICON_SIZE_SMALL).'</a>&nbsp;';
-        $return .= '<a href="'.api_get_path(WEB_CODE_PATH).'survey/survey_invite.php?'.api_get_cidreq().'&survey_id='.$survey_id.'">'.
-            Display::return_icon('mail_send.png', get_lang('Publish'), '', ICON_SIZE_SMALL)
-            .'</a>&nbsp;';
-        $return .= '<a href="'.api_get_path(WEB_CODE_PATH).'survey/survey_list.php?'.api_get_cidreq().'&action=empty&survey_id='.$survey_id.'" onclick="javascript: if(!confirm(\''
-            .addslashes(api_htmlentities(get_lang("EmptySurvey").'?', ENT_QUOTES)).'\')) return false;">'
-            .Display::return_icon('clean.png', get_lang('EmptySurvey'), '', ICON_SIZE_SMALL).'</a>&nbsp;';
+        $actions = [];
+        $codePath = api_get_path(WEB_CODE_PATH);
+        $params = [];
+        parse_str(api_get_cidreq(), $params);
+        $actions[] = Display::url(
+            Display::return_icon('preview_view.png', get_lang('Preview')),
+            $codePath.'survey/preview.php?'.http_build_query($params + ['survey_id' => $survey_id])
+        );
+        $actions[] = Display::url(
+            Display::return_icon('mail_send.png', get_lang('Publish')),
+            $codePath.'survey/survey_invite.php?'.http_build_query($params + ['survey_id' => $survey_id])
+        );
+        $warning = addslashes(api_htmlentities(get_lang("EmptySurvey").'?', ENT_QUOTES));
+        $actions[] = Display::url(
+            Display::return_icon('clean.png', get_lang('EmptySurvey')),
+            $codePath.'survey/survey_list.php?'
+                .http_build_query($params + ['action' => 'empty', 'survey_id' => $survey_id]),
+            [
+                'onclick' => "javascript: if(!confirm('".$warning."')) return false;",
+            ]
+        );
 
-        return $return;
+        return implode(PHP_EOL, $actions);
     }
 
     /**
@@ -3076,7 +3108,7 @@ class SurveyUtil
                 survey.avail_till AS col6,
                 survey.invited AS col7,
                 survey.anonymous AS col8,
-                survey.survey_id AS col9,
+                survey.iid AS col9,
                 survey.session_id AS session_id,
                 survey.answered,
                 survey.invited
@@ -3088,8 +3120,7 @@ class SurveyUtil
             WHERE survey.c_id = $course_id
             $search_restriction
             $condition_session 
-            GROUP BY 
-                survey.survey_id, survey_question.question_id
+            GROUP BY survey.survey_id
             ORDER BY col$column $direction 
             LIMIT $from,$number_of_items
         ";
@@ -3724,4 +3755,35 @@ class SurveyUtil
 
         return $response > 0;
     }
+
+    /**
+     * Get the pending surveys for a user.
+     *
+     * @param int $userId
+     *
+     * @return array
+     */
+    public static function getUserPendingInvitations($userId)
+    {
+        $now = api_get_utc_datetime(null, false, true);
+
+        $dql = "
+            SELECT s, si FROM ChamiloCourseBundle:CSurvey s
+            INNER JOIN ChamiloCourseBundle:CSurveyInvitation si
+                WITH (s.code = si.surveyCode AND s.cId = si.cId AND s.sessionId = si.sessionId )
+            WHERE 
+                si.user = :user_id AND 
+                s.availFrom <= :now AND 
+                s.availTill >= :now AND 
+                si.answered = 0
+            ORDER BY s.availTill ASC
+        ";
+
+        $pendingSurveys = Database::getManager()
+            ->createQuery($dql)
+            ->setParameters(['user_id' => $userId, 'now' => $now->format('Y-m-d')])
+            ->getResult();
+
+        return $pendingSurveys;
+    }
 }

+ 4 - 2
main/ticket/assign_tickets.php

@@ -49,7 +49,8 @@ echo '<div class="row">
 echo '<div class="row"><div class="formw"><select name ="work_id" id="work_id">';
 echo '<option value="0"'.(($row['colid'] == $rs->work_id) ? "selected" : "").'>'.get_lang('PleaseSelect').'</option>';
 while ($row = Database::fetch_assoc($result_tasks)) {
-    echo '<option value="'.$row['colid'].'"'.(($row['colid'] == $rs->work_id) ? "selected" : "").'>'.$row['coltitle'].'</option>';
+    echo '<option value="'.$row['colid'].'"'.(($row['colid'] == $rs->work_id) ? "selected" : "").'>'.
+        $row['coltitle'].'</option>';
 }
 echo '</select></div><div>';
 echo '<div class="row">
@@ -58,7 +59,8 @@ echo '<div class="row">
 echo '<div class="row"><div class="formw"><select name ="forum_id" id="forum_id">';
 echo '<option value="0"'.(($row['colid'] == $rs->work_id) ? "forum_id" : "").'>'.get_lang('PleaseSelect').'</option>';
 while ($row = Database::fetch_assoc($result_forum)) {
-    echo '<option value="'.$row['colid'].'"'.(($row['colid'] == $rs->forum_id) ? "selected" : "").'>'.$row['coltitle'].'</option>';
+    echo '<option value="'.$row['colid'].'"'.(($row['colid'] == $rs->forum_id) ? "selected" : "").'>'.
+        $row['coltitle'].'</option>';
 }
 echo '</select></div><div>';
 echo '<div class="row">

+ 1 - 1
main/ticket/new_ticket.php

@@ -2,7 +2,7 @@
 /* For licensing terms, see /license.txt */
 
 /**
- * @package chamilo.plugin.ticket
+ * @package chamilo.ticket
  */
 $cidReset = true;
 

+ 2 - 1
main/tracking/course_log_events.php

@@ -8,7 +8,8 @@ require_once __DIR__.'/../inc/global.inc.php';
 $this_section = SECTION_COURSES;
 
 // Access restrictions.
-$is_allowedToTrack = api_is_platform_admin() || api_is_allowed_to_create_course() || api_is_session_admin() || api_is_drh() || api_is_course_tutor();
+$is_allowedToTrack = api_is_platform_admin() || api_is_allowed_to_create_course() ||
+    api_is_session_admin() || api_is_drh() || api_is_course_tutor();
 
 if (!$is_allowedToTrack) {
     api_not_allowed(true);

+ 12 - 11
main/tracking/course_log_groups.php

@@ -14,7 +14,8 @@ if ($from == 'myspace') {
 }
 
 // Access restrictions.
-$is_allowedToTrack = api_is_platform_admin() || api_is_allowed_to_create_course() || api_is_session_admin() || api_is_drh() || api_is_course_tutor();
+$is_allowedToTrack = api_is_platform_admin() || api_is_allowed_to_create_course() ||
+    api_is_session_admin() || api_is_drh() || api_is_course_tutor();
 
 if (!$is_allowedToTrack) {
     api_not_allowed(true);
@@ -108,16 +109,16 @@ $htmlHeadXtra[] = api_get_jqgrid_js();
 $htmlHeadXtra[] = '
 <script>
 $(function() {
-    '.Display::grid_js(
-        'group_users',
-        $url,
-        $columns,
-        $column_model,
-        $extra_params,
-        [],
-        $action_links,
-        true
-    ).'
+'.Display::grid_js(
+    'group_users',
+    $url,
+    $columns,
+    $column_model,
+    $extra_params,
+    [],
+    $action_links,
+    true
+).'
 });
 </script>';
 

+ 2 - 1
main/tracking/course_log_resources.php

@@ -18,7 +18,8 @@ if ($from == 'myspace') {
 }
 
 // Access restrictions.
-$is_allowedToTrack = api_is_platform_admin() || api_is_allowed_to_create_course() || api_is_session_admin() || api_is_drh() || api_is_course_tutor();
+$is_allowedToTrack = api_is_platform_admin() || api_is_allowed_to_create_course() ||
+    api_is_session_admin() || api_is_drh() || api_is_course_tutor();
 
 if (!$is_allowedToTrack) {
     api_not_allowed(true);

+ 2 - 1
main/tracking/course_log_tools.php

@@ -22,7 +22,8 @@ if ($from == 'myspace') {
 }
 
 // Access restrictions.
-$is_allowedToTrack = api_is_platform_admin() || api_is_allowed_to_create_course() || api_is_session_admin() || api_is_drh() || api_is_course_tutor();
+$is_allowedToTrack = api_is_platform_admin() || api_is_allowed_to_create_course() ||
+    api_is_session_admin() || api_is_drh() || api_is_course_tutor();
 
 if (!$is_allowedToTrack) {
     api_not_allowed();

+ 77 - 0
main/tracking/messages.php

@@ -0,0 +1,77 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+require_once __DIR__.'/../inc/global.inc.php';
+
+$allow = api_get_configuration_value('allow_user_message_tracking');
+
+if (!$allow) {
+    api_not_allowed(true);
+}
+
+$allowUser = api_is_platform_admin() || api_is_drh();
+
+if (!$allowUser) {
+    api_not_allowed(true);
+}
+
+$fromUserId = isset($_GET['from_user']) ? (int) $_GET['from_user'] : 0;
+$toUserId = isset($_GET['to_user']) ? (int) $_GET['to_user'] : 0;
+if (empty($fromUserId) || empty($toUserId)) {
+    api_not_allowed(true);
+}
+
+if (api_is_drh()) {
+    $isFollowed = UserManager::is_user_followed_by_drh($fromUserId, api_get_user_id());
+    if (!$isFollowed) {
+        api_not_allowed(true);
+    }
+}
+
+$usersData[$toUserId] = api_get_user_info($toUserId);
+$usersData[$fromUserId] = api_get_user_info($fromUserId);
+$messages = MessageManager::getAllMessagesBetweenStudents($toUserId, $fromUserId);
+
+$content = Display::page_subheader2(sprintf(
+    get_lang('MessagesExchangeBetweenXAndY'),
+    $usersData[$toUserId]['complete_name'],
+    $usersData[$fromUserId]['complete_name']
+));
+
+$interbreadcrumb[] = [
+    'url' => api_get_path(WEB_CODE_PATH).'mySpace/student.php',
+    'name' => get_lang('MyStudents'),
+];
+$interbreadcrumb[] = [
+    'url' => api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?student='.$fromUserId,
+    'name' => get_lang('StudentDetails'),
+];
+
+$uniqueMessageList = [];
+foreach ($messages as $message) {
+    $message['title'].
+    $subText = get_lang('From').': '.$usersData[$message['user_sender_id']]['complete_name'];
+    $title = empty($message['title']) ? get_lang('Untitled') : $message['title'];
+    $title = $title.' - '.$subText.'<span class="pull-right">'.Display::dateToStringAgoAndLongDate($message['send_date']).'</span>';
+    $messageId = $message['id'];
+
+    $hash = sha1($message['title'].$message['content'].$message['send_date']);
+    if (in_array($hash, $uniqueMessageList)) {
+        continue;
+    }
+
+    $content .= Display::panelCollapse(
+        $title,
+        $message['content'].'<br />'.Display::dateToStringAgoAndLongDate($message['send_date']),
+        'message-'.$message['id'],
+        null,
+        'message-'.$message['id'],
+        'collapse-'.$message['id'],
+        false
+    );
+    $uniqueMessageList[] = $hash;
+}
+
+$template = new Template(get_lang('MessageTracking'));
+$template->assign('content', $content);
+$template->display_one_col_template();

+ 10 - 10
main/upload/form.document.php

@@ -15,23 +15,23 @@
 $nameTools = get_lang('FileUpload');
 $interbreadcrumb[] = ["url" => "../lp/lp_controller.php?action=list", "name" => get_lang(TOOL_DOCUMENT)];
 Display::display_header($nameTools, "Doc");
-//show the title
+// Show the title
 api_display_tool_title($nameTools.$add_group_to_title);
 ?>
 
 <div id="dynamic_div" style="display:block;margin-left:40%;margin-top:10px;height:50px;">
 </div>
 <div id="upload_form_div" name="form_div" style="display:block;">
-	<form method="POST" action="upload.php" id="upload_form" enctype="multipart/form-data" onsubmit="javascript: myUpload.start('dynamic_div','progressbar_green.gif','<?php echo get_lang('Uploading', ''); ?>','upload_form_div');">
-		<input type="hidden" name="curdirpath" value="<?php echo $path; ?>">
-		<input type="hidden" name="tool" value="<?php echo $my_tool; ?>">
-		<input type="file" name="user_file">
-		<input type="submit" name="submit" value="Upload">
-	</form>
+    <form method="POST" action="upload.php" id="upload_form"
+          enctype="multipart/form-data"
+          onsubmit="javascript: myUpload.start('dynamic_div','progressbar_green.gif','<?php echo get_lang('Uploading', ''); ?>', 'upload_form_div');">
+        <input type="hidden" name="curdirpath" value="<?php echo $path; ?>">
+        <input type="hidden" name="tool" value="<?php echo $my_tool; ?>">
+        <input type="file" name="user_file">
+        <input type="submit" name="submit" value="Upload">
+    </form>
 </div>
 <br/>
 <?php
-/*
-        FOOTER
-*/
+
 Display::display_footer();

+ 0 - 38
main/upload/form.scorm.php

@@ -87,46 +87,8 @@ if (api_get_setting('search_enabled') == 'true') {
 if (api_is_platform_admin()) {
     $form->addElement('checkbox', 'use_max_score', null, get_lang('UseMaxScore100'));
 }
-
-/* This is a special section that has to be enabled in specific cases
- * PLEASE DO NOT REMOVE
-$list = get_zip_files_in_garbage();
-if (count($list)>0) {
-    $select_file_name = &$form->addElement(
-        'select',
-        'file_name',
-        get_lang('Or').' '.api_strtolower(get_lang('UploadLocalFileFromGarbageDir'))
-    );
-    foreach($list as $file){
-        $select_file_name->addOption($file, $file);
-    }
-    $form->addElement('submit', 'submit', get_lang('Download'));
-} else {
-    $text_empty = &$form->addElement(
-        'text',
-        'empty',
-        get_lang('Or').' '.api_strtolower(get_lang('UploadLocalFileFromGarbageDir'))
-    );
-    $defaults["empty"] = get_lang('Empty');
-    $text_empty->freeze();
-}*/
-
 $form->addButtonUpload(get_lang('Upload'));
 
-/*
-TODO: check the pens plugin is enabled before using it
-if (is_dir(api_get_path(PLUGIN_PATH)."/pens")) {
-    require_once api_get_path(PLUGIN_PATH)."/pens/chamilo_pens.php";
-    $list = ChamiloPens::findAll();
-    if (count($list) > 0) {
-        $select_pens = $form->addElement('select', 'pens_package', get_lang('Or').' '.get_lang('select a PENS package'));
-        foreach ($list as $package) {
-            $select_pens->addOption($package->getPackageName(), $package->getPackageName());
-        }
-    }
-}
-*/
-
 // the default values for the form
 $defaults = ['index_document' => 'checked="checked"', 'use_max_score' => 1];
 $form->setDefaults($defaults);

+ 7 - 1
main/upload/upload_ppt.php

@@ -33,7 +33,13 @@ if (isset($_POST['convert'])) {
                             foreach ($values as $value) {
                                 $value = trim($value);
                                 if (!empty($value)) {
-                                    add_specific_field_value($specific_field['id'], api_get_course_id(), TOOL_LEARNPATH, $o_ppt->lp_id, $value);
+                                    add_specific_field_value(
+                                        $specific_field['id'],
+                                        api_get_course_id(),
+                                        TOOL_LEARNPATH,
+                                        $o_ppt->lp_id,
+                                        $value
+                                    );
                                 }
                             }
                         }

+ 13 - 4
main/upload/upload_word.php

@@ -51,7 +51,13 @@ if (isset($_POST['convert'])) {
                             foreach ($values as $value) {
                                 $value = trim($value);
                                 if (!empty($value)) {
-                                    add_specific_field_value($specific_field['id'], api_get_course_id(), TOOL_LEARNPATH, $o_doc->lp_id, $value);
+                                    add_specific_field_value(
+                                        $specific_field['id'],
+                                        api_get_course_id(),
+                                        TOOL_LEARNPATH,
+                                        $o_doc->lp_id,
+                                        $value
+                                    );
                                 }
                             }
                         }
@@ -82,7 +88,8 @@ $interbreadcrumb[] = ["url" => "../lp/lp_controller.php?action=list", "name" =>
 $nameTools = get_lang("WoogieConversionPowerPoint");
 Display :: display_header($nameTools);
 
-echo '<span style="color: #5577af; font-size: 16px; font-family: Arial; margin-left: 10px;">'.get_lang("WelcomeWoogieSubtitle").'</span><br>';
+echo '<span style="color: #5577af; font-size: 16px; font-family: Arial; margin-left: 10px;">'.
+    get_lang("WelcomeWoogieSubtitle").'</span><br>';
 $message = get_lang("WelcomeWoogieConverter");
 echo '<br />';
 $s_style = "border-width: 1px;
@@ -112,11 +119,13 @@ $s_style_error = "border-width: 1px;
          color: #000;";
 
 echo '<div style="'.$s_style.'"><div style="float:left; margin-right:10px;">
-<img src="'.Display::returnIconPath('message_normal.gif').'" alt="'.$alt_text.'" '.$attribute_list.'  /></div><div style="margin-left: 43px">'.$message.'</div></div>';
+<img src="'.Display::returnIconPath('message_normal.gif').'" alt="'.$alt_text.'" '.$attribute_list.'  /></div>
+<div style="margin-left: 43px">'.$message.'</div></div>';
 
 if (!empty($errorMessage)) {
     echo '<div style="'.$s_style_error.'"><div style="float:left; margin-right:10px;">
-    <img src="'.Display::returnIconPath('message_error.gif').'" alt="'.$alt_text.'" '.$attribute_list.'  /></div><div style="margin-left: 43px">'.$errorMessage.'</div></div>';
+    <img src="'.Display::returnIconPath('message_error.gif').'" alt="'.$alt_text.'" '.$attribute_list.'  /></div>
+    <div style="margin-left: 43px">'.$errorMessage.'</div></div>';
 }
 
 $form = new FormValidator('update_course', 'POST', '', '', 'style="margin: 0;"');

+ 6 - 11
main/user/add_users_to_session.php

@@ -216,14 +216,12 @@ if ($allowTutors == 'true') {
     }
 
     $xajax->processRequests();
-
     $htmlHeadXtra[] = $xajax->getJavascript('../inc/lib/xajax/');
     $htmlHeadXtra[] = '<script>
     function add_user_to_session (code, content) {
         document.getElementById("user_to_add").value = "";
         document.getElementById("ajax_list_users_single").innerHTML = "";
         destination = document.getElementById("destination_users");
-
         for (i=0;i<destination.length;i++) {
             if(destination.options[i].text == content) {
                     return false;
@@ -260,9 +258,7 @@ if ($allowTutors == 'true') {
         $("#user_with_any_session_id").attr("checked", false);
         xajax_search_users(val,"multiple");
     }
-
     </script>';
-
     $form_sent = 0;
     $firstLetterUser = $firstLetterSession = '';
     $UserList = $SessionList = [];
@@ -287,9 +283,7 @@ if ($allowTutors == 'true') {
 
     $session_info = SessionManager::fetch($id_session);
     Display::display_header($tool_name);
-
     $nosessionUsersList = $sessionUsersList = [];
-
     $ajax_search = $add_type === 'unique' ? true : false;
 
     $order_clause = api_sort_by_first_name() ? ' ORDER BY firstname, lastname, username' : ' ORDER BY lastname, firstname, username';
@@ -472,18 +466,21 @@ if ($allowTutors == 'true') {
     }
 
     if ($add_type === 'multiple') {
-        $link_add_type_unique = '<a href="'.api_get_self().'?id_session='.$id_session.'&add='.Security::remove_XSS($_GET['add']).'&add_type=unique">'.Display::return_icon('single.gif').get_lang('SessionAddTypeUnique').'</a>';
+        $link_add_type_unique = '<a href="'.api_get_self().'?id_session='.$id_session.'&add='.Security::remove_XSS($_GET['add']).'&add_type=unique">'.
+            Display::return_icon('single.gif').get_lang('SessionAddTypeUnique').'</a>';
         $link_add_type_multiple = Display::return_icon('multiple.gif').get_lang('SessionAddTypeMultiple');
     } else {
         $link_add_type_unique = Display::return_icon('single.gif').get_lang('SessionAddTypeUnique');
-        $link_add_type_multiple = '<a href="'.api_get_self().'?id_session='.$id_session.'&add='.Security::remove_XSS($_GET['add']).'&add_type=multiple">'.Display::return_icon('multiple.gif').get_lang('SessionAddTypeMultiple').'</a>';
+        $link_add_type_multiple = '<a href="'.api_get_self().'?id_session='.$id_session.'&add='.Security::remove_XSS($_GET['add']).'&add_type=multiple">'.
+            Display::return_icon('multiple.gif').get_lang('SessionAddTypeMultiple').'</a>';
     }
     $link_add_group = '<a href="usergroups.php">'.
             Display::return_icon('multiple.gif', get_lang('RegistrationByUsersGroups')).get_lang('RegistrationByUsersGroups').'</a>'; ?>
     <div class="actions">
         <?php echo $link_add_type_unique; ?>&nbsp;|&nbsp;<?php echo $link_add_type_multiple; ?>&nbsp;|&nbsp;<?php echo $link_add_group; ?>
     </div>
-    <form name="formulaire" method="post" action="<?php echo api_get_self(); ?>?page=<?php echo $page; ?>&id_session=<?php echo $id_session; ?><?php if (!empty($_GET['add'])) {
+    <form name="formulaire" method="post"
+          action="<?php echo api_get_self(); ?>?page=<?php echo $page; ?>&id_session=<?php echo $id_session; ?><?php if (!empty($_GET['add'])) {
                 echo '&add=true';
             } ?>" style="margin:0px;" <?php if ($ajax_search) {
                 echo ' onsubmit="valide();"';
@@ -522,7 +519,6 @@ if ($allowTutors == 'true') {
         <div class="span5">
             <div class="multiple_select_header">
                 <b><?php echo get_lang('UserListInPlatform'); ?> :</b>
-
             <?php if ($add_type == 'multiple') {
         ?>
                 <?php echo get_lang('FirstLetterUser'); ?> :
@@ -652,7 +648,6 @@ if ($allowTutors == 'true') {
         document.forms.formulaire.submit();
     }
 
-
     function loadUsersInSelect(select)
     {
         var xhr_object = null;

+ 2 - 2
main/user/classes.php

@@ -46,8 +46,8 @@ if (!empty($usergroup_list)) {
     }
 }
 
-Display :: display_header($tool_name, 'Classes');
+Display::display_header($tool_name, 'Classes');
 
 echo $content;
 
-Display :: display_footer();
+Display::display_footer();

+ 1 - 2
main/user/resume_session.php

@@ -238,8 +238,7 @@ if ($allowTutors === 'true') {
         $result = Database::query($sql);
         $courses = Database::store_result($result);
         foreach ($courses as $course) {
-            //select the number of users
-
+            // Select the number of users
             $sql = "SELECT count(*) FROM $tbl_session_rel_user sru, $tbl_session_rel_course_rel_user srcru
                     WHERE
                         srcru.user_id = sru.user_id AND

+ 12 - 3
main/work/add_user.php

@@ -72,8 +72,13 @@ if (!empty($items)) {
         $usersAdded[] = $myUserId;
         $userInfo = api_get_user_info($myUserId);
         $url = api_get_path(WEB_CODE_PATH).'work/add_user.php?action=delete&id='.$workId.'&user_id='.$myUserId;
-        $link = Display::url('<em class="fa fa-trash"></em> '.get_lang('Delete'), $url, ['class' => 'btn btn-danger btn-sm']);
-        echo '<li class="list-group-item">'.$userInfo['complete_name_with_username'].'<div class="pull-right">'.$link.'</div></li>';
+        $link = Display::url(
+            '<em class="fa fa-trash"></em> '.get_lang('Delete'),
+            $url,
+            ['class' => 'btn btn-danger btn-sm']
+        );
+        echo '<li class="list-group-item">'.
+                $userInfo['complete_name_with_username'].'<div class="pull-right">'.$link.'</div></li>';
     }
     echo '</ul>';
 }
@@ -105,7 +110,11 @@ if (!empty($userToAddList)) {
     foreach ($userToAddList as $user) {
         $userName = api_get_person_name($user['firstname'], $user['lastname']).' ('.$user['username'].') ';
         $url = api_get_path(WEB_CODE_PATH).'work/add_user.php?action=add&id='.$workId.'&user_id='.$user['user_id'];
-        $link = Display::url('<em class="fa fa-plus"></em> '.get_lang('Add'), $url, ['class' => 'btn btn-primary btn-sm']);
+        $link = Display::url(
+            '<em class="fa fa-plus"></em> '.get_lang('Add'),
+            $url,
+            ['class' => 'btn btn-primary btn-sm']
+        );
         echo '<li class="list-group-item">'.$userName.'<div class="pull-right"> '.$link.'</div></li>';
     }
     echo '</ul>';

+ 3 - 4
main/work/download.php

@@ -18,17 +18,16 @@ $this_section = SECTION_COURSES;
 // Course protection
 api_protect_course_script(true);
 
-$id = intval($_GET['id']);
-
+$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
 $courseInfo = api_get_course_info();
 
-if (empty($courseInfo)) {
+if (empty($courseInfo) || empty($id)) {
     api_not_allowed(true);
 }
 
 $correction = isset($_REQUEST['correction']) ? true : false;
 $result = downloadFile($id, $courseInfo, $correction);
-if ($result == false) {
+if ($result === false) {
     api_not_allowed(true);
 }
 

+ 18 - 15
main/work/downloadfolder.inc.php

@@ -8,21 +8,26 @@
  *
  * @package chamilo.work
  */
-$work_id = $_GET['id'];
 require_once __DIR__.'/../inc/global.inc.php';
+
+api_protect_course_script(true);
+
+$workId = isset($_GET['id']) ? (int) $_GET['id'] : 0;
+
 $current_course_tool = TOOL_STUDENTPUBLICATION;
 $_course = api_get_course_info();
 
-// Protection
-api_protect_course_script(true);
+if (empty($_course)) {
+    api_not_allowed();
+}
 
 require_once 'work.lib.php';
 
-$work_data = get_work_data_by_id($work_id);
+$work_data = get_work_data_by_id($workId);
 $groupId = api_get_group_id();
 
 if (empty($work_data)) {
-    exit;
+    api_not_allowed();
 }
 
 // Prevent some stuff.
@@ -87,7 +92,7 @@ if (api_is_allowed_to_edit() || api_is_coach()) {
  			    props.tool = 'work' AND
  			    props.c_id = $course_id AND
                 work.c_id = $course_id AND
-                work.parent_id = $work_id AND
+                work.parent_id = $workId AND
                 work.filetype = 'file' AND
                 props.visibility <> '2' AND
                 work.active IN (0, 1) AND
@@ -96,9 +101,8 @@ if (api_is_allowed_to_edit() || api_is_coach()) {
             ";
 } else {
     $courseInfo = api_get_course_info();
-    protectWork($courseInfo, $work_id);
-
-    $userCondition = null;
+    protectWork($courseInfo, $workId);
+    $userCondition = '';
 
     // All users
     if ($courseInfo['show_score'] == 0) {
@@ -129,7 +133,7 @@ if (api_is_allowed_to_edit() || api_is_coach()) {
                 props.tool = 'work' AND
                 work.accepted = 1 AND
                 work.active = 1 AND
-                work.parent_id = $work_id AND
+                work.parent_id = $workId AND
                 work.filetype = 'file' AND
                 props.visibility = '1' AND
                 work.post_group_id = $groupIid
@@ -140,7 +144,7 @@ $query = Database::query($sql);
 
 //add tem to the zip file
 while ($not_deleted_file = Database::fetch_assoc($query)) {
-    $user_info = api_get_user_info($not_deleted_file['insert_user_id']);
+    $userInfo = api_get_user_info($not_deleted_file['insert_user_id']);
     $insert_date = api_get_local_time($not_deleted_file['sent_date']);
     $insert_date = str_replace([':', '-', ' '], '_', $insert_date);
 
@@ -150,8 +154,8 @@ while ($not_deleted_file = Database::fetch_assoc($query)) {
             $title = $not_deleted_file['filename'];
         }
     }
-
-    $filename = $insert_date.'_'.$user_info['username'].'_'.$title;
+    $filename = $insert_date.'_'.$userInfo['username'].'_'.$title;
+    $filename = api_replace_dangerous_char($filename);
     // File exists
     if (file_exists($sys_course_path.$_course['path'].'/'.$not_deleted_file['url']) &&
         !empty($not_deleted_file['url'])
@@ -198,8 +202,7 @@ if (!empty($files)) {
     exit;
 }
 
-/*	Extra function (only used here) */
-
+/* Extra function (only used here) */
 function my_pre_add_callback($p_event, &$p_header)
 {
     global $files;

+ 6 - 3
main/work/upload_corrections.php

@@ -13,7 +13,7 @@ require_once 'work.lib.php';
 
 $this_section = SECTION_COURSES;
 
-$workId = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : null;
+$workId = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0;
 
 $is_allowed_to_edit = api_is_allowed_to_edit();
 $course_id = api_get_course_int_id();
@@ -29,9 +29,12 @@ if (empty($workId)) {
 }
 
 protectWork($courseInfo, $workId);
-
 $workInfo = get_work_data_by_id($workId);
 
+if (empty($workInfo)) {
+    api_not_allowed(true);
+}
+
 $student_can_edit_in_session = api_is_allowed_to_session_edit(false, true);
 
 $homework = get_work_assignment_by_id($workInfo['id']);
@@ -102,7 +105,7 @@ if ($form->validate()) {
         $destinationDir = api_get_path(SYS_ARCHIVE_PATH).$folder;
         mkdir($destinationDir, api_get_permissions_for_new_directories(), true);
 
-        /*	Uncompress zip file*/
+        // Uncompress zip file
         // We extract using a callback function that "cleans" the path
         $result = $zip->extract(
             PCLZIP_OPT_PATH,

+ 85 - 312
main/work/work.lib.php

@@ -47,12 +47,12 @@ function displayWorkActionLinks($id, $action, $isTutor)
         if (empty($id)) {
             $output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=create_dir">';
             $output .= Display::return_icon(
-                    'new_work.png',
-                    get_lang('CreateAssignment'),
-                    '',
-                    ICON_SIZE_MEDIUM
-                ).
-                '</a>';
+                'new_work.png',
+                get_lang('CreateAssignment'),
+                '',
+                ICON_SIZE_MEDIUM
+            );
+            $output .= '</a>';
         }
     }
 
@@ -77,18 +77,6 @@ function displayWorkActionLinks($id, $action, $isTutor)
             '</a>';
     }
 
-    if (api_is_allowed_to_edit(null, true) &&
-        $origin != 'learnpath' &&
-        api_is_allowed_to_session_edit(false, true)
-    ) {
-        // Delete all files
-        if (api_get_setting('permanently_remove_deleted_files') == 'true') {
-            $message = get_lang('ConfirmYourChoiceDeleteAllfiles');
-        } else {
-            $message = get_lang('ConfirmYourChoice');
-        }
-    }
-
     if ($output != '') {
         echo '<div class="actions">';
         echo $output;
@@ -108,9 +96,8 @@ function displayWorkActionLinks($id, $action, $isTutor)
  */
 function settingsForm($defaults)
 {
-    $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
-
-    if (!$is_allowed_to_edit) {
+    $allowed = api_is_allowed_to_edit(null, true);
+    if (!$allowed) {
         return;
     }
 
@@ -150,8 +137,8 @@ function get_work_data_by_path($path, $courseId = 0)
         $courseId = api_get_course_int_id();
     }
 
-    $work_table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
-    $sql = "SELECT *  FROM  ".$work_table."
+    $table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
+    $sql = "SELECT *  FROM $table
             WHERE url = '$path' AND c_id = $courseId ";
     $result = Database::query($sql);
     $return = [];
@@ -178,7 +165,7 @@ function get_work_data_by_id($id, $courseId = 0, $sessionId = 0)
     }
     $table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
 
-    $sessionCondition = null;
+    $sessionCondition = '';
     if (!empty($sessionId)) {
         $sessionCondition = api_get_session_condition($sessionId, true);
     }
@@ -257,15 +244,13 @@ function get_work_count_by_student($user_id, $work_id)
  *
  * @return array
  */
-function get_work_assignment_by_id($id, $courseId = null)
+function get_work_assignment_by_id($id, $courseId = 0)
 {
+    $courseId = intval($courseId);
     if (empty($courseId)) {
         $courseId = api_get_course_int_id();
-    } else {
-        $courseId = intval($courseId);
     }
     $id = intval($id);
-
     $table = Database::get_course_table(TABLE_STUDENT_PUBLICATION_ASSIGNMENT);
     $sql = "SELECT * FROM $table
             WHERE c_id = $courseId AND publication_id = $id";
@@ -299,11 +284,12 @@ function getWorkList($id, $my_folder_data, $add_in_where_query = null, $course_i
     $groupIid = 0;
     if ($group_id) {
         $groupInfo = GroupManager::get_group_properties($group_id);
-        $groupIid = $groupInfo['iid'];
+        if ($groupInfo) {
+            $groupIid = $groupInfo['iid'];
+        }
     }
 
     $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
-
     $linkInfo = GradebookUtils::isResourceInCourseGradebook(
         api_get_course_id(),
         3,
@@ -326,7 +312,6 @@ function getWorkList($id, $my_folder_data, $add_in_where_query = null, $course_i
     }
 
     $contains_file_query = '';
-
     // Get list from database
     if ($is_allowed_to_edit) {
         $active_condition = ' active IN (0, 1)';
@@ -604,9 +589,6 @@ function showTeacherWorkGrid()
         ['name' => 'amount', 'index' => 'amount', 'width' => '110', 'align' => 'center', 'sortable' => 'false'],
         ['name' => 'actions', 'index' => 'actions', 'width' => '110', 'align' => 'left', 'sortable' => 'false'],
     ];
-
-    $token = null;
-
     $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_work_teacher&'.api_get_cidreq();
     $deleteUrl = api_get_path(WEB_AJAX_PATH).'work.ajax.php?a=delete_work&'.api_get_cidreq();
 
@@ -659,7 +641,8 @@ function showTeacherWorkGrid()
 function build_work_directory_selector($folders, $curdirpath, $group_dir = '')
 {
     $form = '<form name="selector" action="'.api_get_self().'?'.api_get_cidreq().'" method="POST">';
-    $form .= get_lang('CurrentDirectory').' <select name="curdirpath" onchange="javascript: document.selector.submit();">';
+    $form .= get_lang('CurrentDirectory').' 
+             <select name="curdirpath" onchange="javascript: document.selector.submit();">';
     //group documents cannot be uploaded in the root
     if ($group_dir == '') {
         $form .= '<option value="/">/ ('.get_lang('Root').')</option>';
@@ -686,7 +669,7 @@ function build_work_directory_selector($folders, $curdirpath, $group_dir = '')
 }
 
 /**
- * Builds the form thats enables the user to
+ * Builds the form that enables the user to
  * move a document from one directory to another
  * This function has been copied from the document/document.inc.php library.
  *
@@ -718,19 +701,18 @@ function build_work_move_to_selector($folders, $curdirpath, $move_file, $group_d
     $form->addHidden('item_id', $move_file);
     $form->addHidden('action', 'move_to');
 
-    //group documents cannot be uploaded in the root
+    // Group documents cannot be uploaded in the root
     if ($group_dir == '') {
-        if ($curdirpath != '/') {
-            //$form .= '<option value="0">/ ('.get_lang('Root').')</option>';
-        }
         if (is_array($folders)) {
             foreach ($folders as $fid => $folder) {
                 //you cannot move a file to:
                 //1. current directory
                 //2. inside the folder you want to move
                 //3. inside a subfolder of the folder you want to move
-                if (($curdirpath != $folder) && ($folder != $move_file) && (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')) {
-                    //$form .= '<option value="'.$fid.'">'.$folder.'</option>';
+                if (($curdirpath != $folder) &&
+                    ($folder != $move_file) &&
+                    (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
+                ) {
                     $options[$fid] = $folder;
                 }
             }
@@ -740,7 +722,9 @@ function build_work_move_to_selector($folders, $curdirpath, $move_file, $group_d
             $form .= '<option value="0">/ ('.get_lang('Root').')</option>';
         }
         foreach ($folders as $fid => $folder) {
-            if (($curdirpath != $folder) && ($folder != $move_file) && (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')) {
+            if (($curdirpath != $folder) && ($folder != $move_file) &&
+                (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
+            ) {
                 //cannot copy dir into his own subdir
                 $display_folder = substr($folder, strlen($group_dir));
                 $display_folder = ($display_folder == '') ? '/ ('.get_lang('Root').')' : $display_folder;
@@ -812,18 +796,15 @@ function deleteDirWork($id)
     $base_work_dir = api_get_path(SYS_COURSE_PATH).$_course['path'].'/work';
     $work_data_url = $base_work_dir.$work_data['url'];
     $check = Security::check_abs_path($work_data_url.'/', $base_work_dir.'/');
-
     $table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
     $TSTDPUBASG = Database::get_course_table(TABLE_STUDENT_PUBLICATION_ASSIGNMENT);
     $t_agenda = Database::get_course_table(TABLE_AGENDA);
-
     $course_id = api_get_course_int_id();
     $sessionId = api_get_session_id();
 
     if (!empty($work_data['url'])) {
         if ($check) {
             $consideredWorkingTime = api_get_configuration_value('considered_working_time');
-
             if (!empty($consideredWorkingTime)) {
                 $fieldValue = new ExtraFieldValue('work');
                 $resultExtra = $fieldValue->getAllValuesForAnItem(
@@ -832,7 +813,6 @@ function deleteDirWork($id)
                 );
 
                 $workingTime = null;
-
                 foreach ($resultExtra as $field) {
                     $field = $field['value'];
                     if ($consideredWorkingTime == $field->getField()->getVariable()) {
@@ -843,7 +823,6 @@ function deleteDirWork($id)
                 }
 
                 $courseUsers = CourseManager::get_user_list_from_course_code($_course['code'], $sessionId);
-
                 if (!empty($workingTime)) {
                     foreach ($courseUsers as $user) {
                         $userWorks = get_work_user_list(
@@ -862,7 +841,6 @@ function deleteDirWork($id)
                         if (count($userWorks) != 1) {
                             continue;
                         }
-
                         Event::eventRemoveVirtualCourseTime($course_id, $user['user_id'], $sessionId, $workingTime);
                     }
                 }
@@ -919,14 +897,14 @@ function deleteDirWork($id)
                 $sessionId
             );
 
-            $link_info = GradebookUtils::isResourceInCourseGradebook(
+            $linkInfo = GradebookUtils::isResourceInCourseGradebook(
                 api_get_course_id(),
                 3,
                 $id,
                 api_get_session_id()
             );
-            $link_id = $link_info['id'];
-            if ($link_info !== false) {
+            $link_id = $linkInfo['id'];
+            if ($linkInfo !== false) {
                 GradebookUtils::remove_resource_from_course_gradebook($link_id);
             }
 
@@ -1031,151 +1009,6 @@ function updateDirName($work_data, $newPath)
     }
 }
 
-/**
- * Return an array with all the folder's ids that are in the given path.
- *
- * @param   string Path of the directory
- *
- * @return array The list of ids of all the directories in the path
- *
- * @author  Julio Montoya
- *
- * @version April 2008
- */
-function get_parent_directories($id)
-{
-    $course_id = api_get_course_int_id();
-    $em = Database::getManager();
-
-    $directories = $em
-        ->getRepository('ChamiloCourseBundle:CStudentPublication')
-        ->findBy([
-            'cId' => $course_id,
-            'parentId' => $id,
-        ]);
-
-    $list_id = [];
-    foreach ($directories as $directory) {
-        $list_id[] = $directory->getId();
-    }
-
-    return $list_id;
-}
-
-/**
- * Transform an all directory structure (only directories) in an array.
- *
- * @param   string path of the directory
- *
- * @return array the directory structure into an array
- *
- * @author  Julio Montoya
- *
- * @version April 2008
- */
-function directory_to_array($directory)
-{
-    $array_items = [];
-    if ($handle = @opendir($directory)) {
-        while (false !== ($file = readdir($handle))) {
-            if ($file != '.' && $file != '..') {
-                if (is_dir($directory.'/'.$file)) {
-                    $array_items = array_merge($array_items, directory_to_array($directory.'/'.$file));
-                    $file = $directory.'/'.$file;
-                    $array_items[] = preg_replace("/\/\//si", '/', $file);
-                }
-            }
-        }
-        closedir($handle);
-    }
-
-    return $array_items;
-}
-
-/**
- * Insert into the DB of the course all the directories.
- *
- * @param string $base_work_dir path of the /work directory of the course
- *
- * @return mixed Int -1 on error, sql query result on success
- *
- * @author  Julio Montoya
- *
- * @version April 2008
- */
-function insert_all_directory_in_course_table($base_work_dir)
-{
-    $dir_to_array = directory_to_array($base_work_dir, true);
-    $only_dir = [];
-
-    for ($i = 0; $i < count($dir_to_array); $i++) {
-        $only_dir[] = substr($dir_to_array[$i], strlen($base_work_dir), strlen($dir_to_array[$i]));
-    }
-    $course_id = api_get_course_int_id();
-    $group_id = api_get_group_id();
-    $work_table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
-    $groupIid = 0;
-    if ($group_id) {
-        $groupInfo = GroupManager::get_group_properties($group_id);
-        $groupIid = $groupInfo['iid'];
-    }
-
-    for ($i = 0; $i < count($only_dir); $i++) {
-        $url = $only_dir[$i];
-
-        $params = [
-            'c_id' => $course_id,
-            'url' => $url,
-            'title' => '',
-            'description' => '',
-            'author' => '',
-            'active' => '1',
-            'accepted' => '1',
-            'filetype' => 'folder',
-            'post_group_id' => $groupIid,
-        ];
-
-        Database::insert($work_table, $params);
-    }
-}
-/**
- * This function displays the number of files contained in a directory.
- *
- * @param   string the path of the directory
- * @param   bool true if we want the total quantity of files
- * include in others child directories, false only  files in the directory
- *
- * @return array the first element is an integer with the number of files
- *               in the folder, the second element is the number of directories
- *
- * @author  Julio Montoya
- *
- * @version April 2008
- */
-function count_dir($path_dir, $recurse)
-{
-    $count = 0;
-    $count_dir = 0;
-    $d = dir($path_dir);
-    while ($entry = $d->Read()) {
-        if (!(($entry == '..') || ($entry == '.'))) {
-            if (is_dir($path_dir.'/'.$entry)) {
-                $count_dir++;
-                if ($recurse) {
-                    $count += count_dir($path_dir.'/'.$entry, $recurse);
-                }
-            } else {
-                $count++;
-            }
-        }
-    }
-    $return_array = [];
-    $return_array[] = $count;
-    $return_array[] = $count_dir;
-
-    return $return_array;
-}
-
 /**
  * returns all the javascript that is required for easily
  * validation when you create a work
@@ -1183,11 +1016,9 @@ function count_dir($path_dir, $recurse)
  */
 function to_javascript_work()
 {
-    $js = '<script>
- 
+    $js = '<script> 
         function updateDocumentTitle(value) {
-            var temp = value.indexOf("/");
-            
+            var temp = value.indexOf("/");            
             //linux path
             if(temp != -1){
                 temp=value.split("/");
@@ -1209,67 +1040,15 @@ function to_javascript_work()
             }
             
             document.getElementById("file_upload").value = baseFilename;
-            document.getElementById("file_extension").value = fileExtension;
-            
+            document.getElementById("file_extension").value = fileExtension;                
             $("#contains_file_id").attr("value", 1);
         }
- 
-        function checkDate(month, day, year) {
-          var monthLength =
-            new Array(31,28,31,30,31,30,31,31,30,31,30,31);
-
-          if (!day || !month || !year)
-            return false;
-
-          // check for bisestile year
-          if (year/4 == parseInt(year/4))
-            monthLength[1] = 29;
-
-          if (month < 1 || month > 12)
-            return false;
-
-          if (day > monthLength[month-1])
-            return false;
-
-          return true;
-        }
-
-        function mktime() {
-
-            var no, ma = 0, mb = 0, i = 0, d = new Date(), argv = arguments, argc = argv.length;
-            d.setHours(0,0,0); d.setDate(1); d.setMonth(1); d.setYear(1972);
-
-            var dateManip = {
-                0: function(tt){ return d.setHours(tt); },
-                1: function(tt){ return d.setMinutes(tt); },
-                2: function(tt){ set = d.setSeconds(tt); mb = d.getDate() - 1; return set; },
-                3: function(tt){ set = d.setMonth(parseInt(tt)-1); ma = d.getFullYear() - 1972; return set; },
-                4: function(tt){ return d.setDate(tt+mb); },
-                5: function(tt){ return d.setYear(tt+ma); }
-            };
-
-            for( i = 0; i < argc; i++ ){
-                no = parseInt(argv[i]*1);
-                if (isNaN(no)) {
-                    return false;
-                } else {
-                    // arg is number, lets manipulate date object
-                    if(!dateManip[i](no)){
-                        // failed
-                        return false;
-                    }
-                }
-            }
-            return Math.floor(d.getTime()/1000);
-        }
-
         function setFocus() {
             $("#work_title").focus();
         }
 
         $(document).ready(function() {
             setFocus();
-
             var checked = $("#expiry_date").attr("checked");
             if (checked) {
                 $("#option2").show();                
@@ -1306,7 +1085,8 @@ function to_javascript_work()
  *
  * @return true if is found / false if not found
  */
-// TODO: The name of this function does not fit with the kind of information it returns. Maybe check_work_id() or is_work_id()?
+// TODO: The name of this function does not fit with the kind of information it returns.
+// Maybe check_work_id() or is_work_id()?
 function get_work_id($path)
 {
     $TBL_STUDENT_PUBLICATION = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
@@ -2084,8 +1864,8 @@ function get_work_user_list(
 
     if (!empty($work_data)) {
         if (!empty($group_id)) {
-            // set to select only messages posted by the user's group
             $extra_conditions = " work.post_group_id = '".intval($groupIid)."' ";
+        // set to select only messages posted by the user's group
         } else {
             $extra_conditions = " (work.post_group_id = '0' OR work.post_group_id is NULL) ";
         }
@@ -2270,13 +2050,12 @@ function get_work_user_list(
                 $work['type'] = DocumentManager::build_document_icon_tag('file', $work['url']);
 
                 // File name.
-                $link_to_download = null;
+                $linkToDownload = '';
                 // If URL is present then there's a file to download keep BC.
                 if ($work['contains_file'] || !empty($work['url'])) {
-                    $link_to_download = '<a href="'.$url.'download.php?id='.$item_id.'&'.api_get_cidreq().'">'.$saveIcon.'</a> ';
+                    $linkToDownload = '<a href="'.$url.'download.php?id='.$item_id.'&'.api_get_cidreq().'">'.$saveIcon.'</a> ';
                 }
 
-                $send_to = '';
                 $feedback = '';
                 $count = getWorkCommentCount($item_id, $course_info);
                 if (!is_null($count) && !empty($count)) {
@@ -2309,17 +2088,22 @@ function get_work_user_list(
                 $date = date_to_str_ago($work['sent_date']).' '.$work_date;
                 $work['formatted_date'] = $work_date.' '.$add_string;
                 $work['sent_date_from_db'] = $work['sent_date'];
-                $work['sent_date'] = '<div class="work-date" title="'.$date.'">'.$add_string.' '.Display::dateToStringAgoAndLongDate($work['sent_date']).'</div>';
+                $work['sent_date'] = '<div class="work-date" title="'.$date.'">'.
+                    $add_string.' '.Display::dateToStringAgoAndLongDate($work['sent_date']).'</div>';
                 $work['status'] = $hasCorrection;
                 $work['has_correction'] = $hasCorrection;
 
                 // Actions.
                 $action = '';
                 if (api_is_allowed_to_edit()) {
-                    $action .= '<a href="'.$url.'view.php?'.api_get_cidreq().'&id='.$item_id.'" title="'.get_lang('View').'">'.$rateIcon.'</a> ';
+                    $action .= '<a 
+                        href="'.$url.'view.php?'.api_get_cidreq().'&id='.$item_id.'" 
+                        title="'.get_lang('View').'">'.$rateIcon.'</a> ';
 
                     if ($unoconv && empty($work['contains_file'])) {
-                        $action .= '<a href="'.$url.'work_list_all.php?'.api_get_cidreq().'&id='.$work_id.'&action=export_to_doc&item_id='.$item_id.'" title="'.get_lang('ExportToDoc').'" >'.
+                        $action .= '<a f
+                            href="'.$url.'work_list_all.php?'.api_get_cidreq().'&id='.$work_id.'&action=export_to_doc&item_id='.$item_id.'" 
+                            title="'.get_lang('ExportToDoc').'" >'.
                             Display::return_icon('export_doc.png', get_lang('ExportToDoc'), [], ICON_SIZE_SMALL).'</a> ';
                     }
 
@@ -2379,7 +2163,12 @@ function get_work_user_list(
 
                     if ($locked) {
                         if ($qualification_exists) {
-                            $action .= Display::return_icon('edit_na.png', get_lang('CorrectAndRate'), [], ICON_SIZE_SMALL);
+                            $action .= Display::return_icon(
+                                'edit_na.png',
+                                get_lang('CorrectAndRate'),
+                                [],
+                                ICON_SIZE_SMALL
+                            );
                         } else {
                             $action .= Display::return_icon('edit_na.png', get_lang('Comment'), [], ICON_SIZE_SMALL);
                         }
@@ -2445,7 +2234,7 @@ function get_work_user_list(
                     $qualificator_id = Display::label(get_lang('Revised'), 'success');
                 }
                 $work['qualificator_id'] = $qualificator_id.' '.$hasCorrection;
-                $work['actions'] = '<div class="work-action">'.$send_to.$link_to_download.$action.'</div>';
+                $work['actions'] = '<div class="work-action">'.$linkToDownload.$action.'</div>';
                 $work['correction'] = $correction;
                 $works[] = $work;
             }
@@ -2599,9 +2388,9 @@ function sendEmailToStudentsOnHomeworkCreation($workId, $courseId, $sessionId =
  */
 function is_work_exist_by_url($url)
 {
-    $work_table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
+    $table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
     $url = Database::escape_string($url);
-    $sql = "SELECT id FROM $work_table WHERE url='$url'";
+    $sql = "SELECT id FROM $table WHERE url='$url'";
     $result = Database::query($sql);
     if (Database::num_rows($result) > 0) {
         $row = Database::fetch_row($result);
@@ -2779,8 +2568,6 @@ function get_list_users_without_publication($task_id, $studentId = 0)
  * @param int task id
  * @param int $studentId
  *
- * @return array
- *
  * @author cvargas carlos.vargas@beeznest.com cfasanando, christian.fasanado@beeznest.com
  * @author Julio Montoya <gugli100@gmail.com> Fixes
  */
@@ -2878,7 +2665,9 @@ function getDocumentToWorkPerUser($documentId, $workId, $courseId, $sessionId, $
     $active = intval($active);
     $sessionCondition = api_get_session_condition($sessionId);
 
-    $sql = "SELECT w.* FROM $work w INNER JOIN $workRel rel ON (w.parent_id = rel.work_id)
+    $sql = "SELECT w.* FROM $work w 
+            INNER JOIN $workRel rel 
+            ON (w.parent_id = rel.work_id)
             WHERE
                 w.document_id = $documentId AND
                 w.parent_id = $workId AND
@@ -3387,6 +3176,7 @@ function getLastWorkStudentFromParent(
 /**
  * Get last work information from parent.
  *
+ * @param int   $userId
  * @param array $parentInfo
  * @param array $courseInfo
  * @param int   $sessionId
@@ -3622,6 +3412,10 @@ function addWorkComment($courseInfo, $userId, $parentWork, $work, $data)
     $subject = sprintf(get_lang('ThereIsANewWorkFeedback'), $parentWork['title']);
     $content = sprintf(get_lang('ThereIsANewWorkFeedbackInWorkXHere'), $work['title'], $url);
 
+    if (!empty($data['comment'])) {
+        $content .= '<br /><b>'.get_lang('Comment').':</b><br />'.$data['comment'];
+    }
+
     if (!empty($userIdListToSend)) {
         foreach ($userIdListToSend as $userIdToSend) {
             MessageManager::send_message_simple(
@@ -3696,7 +3490,7 @@ function getWorkCommentForm($work, $workParent)
         }
     }
 
-    Skill::addSkillsToUserForm($form, ITEM_TYPE_STUDENT_PUBLICATION, $workParent['id'], $work['user_id']);
+    Skill::addSkillsToUserForm($form, ITEM_TYPE_STUDENT_PUBLICATION, $workParent['id'], $work['user_id'], $work['id']);
     $form->addHtmlEditor('comment', get_lang('Comment'), false);
     $form->addFile('attachment', get_lang('Attachment'));
     $form->addElement('hidden', 'id', $work['id']);
@@ -3860,14 +3654,14 @@ function uploadWork($my_folder_data, $_course, $isCorrection = false, $workInfo
 
     if (empty($filesize)) {
         return [
-            'error' => Display:: return_message(
+            'error' => Display::return_message(
                 get_lang('UplUploadFailedSizeIsZero'),
                 'error'
             ),
         ];
     } elseif (!filter_extension($new_file_name)) {
         return [
-            'error' => Display:: return_message(
+            'error' => Display::return_message(
                 get_lang('UplUnableToSaveFileFilteredExtension'),
                 'error'
             ),
@@ -3880,7 +3674,7 @@ function uploadWork($my_folder_data, $_course, $isCorrection = false, $workInfo
 
     if ($total_size > $course_max_space) {
         return [
-            'error' => Display :: return_message(get_lang('NoSpace'), 'error'),
+            'error' => Display::return_message(get_lang('NoSpace'), 'error'),
         ];
     }
 
@@ -3912,7 +3706,6 @@ function uploadWork($my_folder_data, $_course, $isCorrection = false, $workInfo
         ];
     }
 
-    $url = null;
     if ($result) {
         $url = 'work/'.$curdirpath.'/'.$new_file_name;
     } else {
@@ -3923,7 +3716,7 @@ function uploadWork($my_folder_data, $_course, $isCorrection = false, $workInfo
         'url' => $url,
         'filename' => $filename,
         'filesize' => $filesize,
-        'error' => null,
+        'error' => '',
     ];
 }
 
@@ -3987,13 +3780,6 @@ function sendAlertToUsers($workId, $courseInfo, $session_id)
     }
 
     if ($send) {
-        $senderEmail = api_get_setting('emailAdministrator');
-        $senderName = api_get_person_name(
-            api_get_setting('administratorName'),
-            api_get_setting('administratorSurname'),
-            null,
-            PERSON_NAME_EMAIL_ADDRESS
-        );
         $subject = "[".api_get_setting('siteName')."] ".get_lang('SendMailBody')."\n ".get_lang('CourseName').": ".$courseInfo['name']."  ";
         foreach ($user_list as $user_data) {
             $to_user_id = $user_data['user_id'];
@@ -4003,7 +3789,6 @@ function sendAlertToUsers($workId, $courseInfo, $session_id)
             $message .= get_lang('DateSent')." : ".api_format_date(api_get_local_time())."\n";
             $url = api_get_path(WEB_CODE_PATH)."work/work.php?cidReq=".$courseInfo['code']."&id_session=".$session_id."&id=".$workData['id'];
             $message .= get_lang('WorkName')." : ".$workData['title']."\n\n".'<a href="'.$url.'">'.get_lang('DownloadLink')."</a>\n";
-            //$message .= $url;
             MessageManager::send_message_simple(
                 $to_user_id,
                 $subject,
@@ -4014,18 +3799,6 @@ function sendAlertToUsers($workId, $courseInfo, $session_id)
                 [],
                 false
             );
-            /*api_mail_html(
-                api_get_person_name(
-                    $user_info['firstname'].' '.$user_info['lastname'],
-                    null,
-                    PERSON_NAME_EMAIL_ADDRESS
-                ),
-                $user_info['email'],
-                $subject,
-                $message,
-                $senderName,
-                $senderEmail
-            );*/
         }
     }
 }
@@ -4040,9 +3813,11 @@ function sendAlertToUsers($workId, $courseInfo, $session_id)
  */
 function checkExistingWorkFileName($filename, $workId)
 {
-    $work_table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
+    $table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
     $filename = Database::escape_string($filename);
-    $sql = "SELECT title FROM $work_table
+    $workId = (int) $workId;
+
+    $sql = "SELECT title FROM $table
             WHERE parent_id = $workId AND title = '$filename' AND active = 1";
     $result = Database::query($sql);
 
@@ -4087,12 +3862,13 @@ function processWorkForm(
         $fileInfo = pathinfo($values['title']);
         if (isset($fileInfo['extension']) && !empty($fileInfo['extension'])) {
             $extension = '.'.$fileInfo['extension'];
+            $values['title'] = $fileInfo['filename'];
         }
     }
 
     $title = $values['title'].$extension;
-    $description = $values['description'];
-    $contains_file = isset($values['contains_file']) && !empty($values['contains_file']) ? intval($values['contains_file']) : 0;
+    $description = isset($values['description']) ? $values['description'] : '';
+    $containsFile = isset($values['contains_file']) && !empty($values['contains_file']) ? (int) $values['contains_file'] : 0;
 
     $saveWork = true;
     $filename = null;
@@ -4101,7 +3877,8 @@ function processWorkForm(
     $workData = [];
     $message = null;
 
-    if ($values['contains_file']) {
+    if ($containsFile) {
+        $saveWork = false;
         if ($checkDuplicated) {
             if (checkExistingWorkFileName($file['name'], $workInfo['id'])) {
                 $saveWork = false;
@@ -4115,11 +3892,13 @@ function processWorkForm(
         }
 
         if (isset($result['error'])) {
+            $saveWork = false;
             if ($showFlashMessage) {
                 $message = $result['error'];
             }
-
-            $saveWork = false;
+            if (empty($result['error']) && isset($result['url']) && !empty($result['url'])) {
+                $saveWork = true;
+            }
         }
     }
 
@@ -4151,7 +3930,7 @@ function processWorkForm(
             'filetype' => 'file',
             'title' => $title,
             'description' => $description,
-            'contains_file' => $contains_file,
+            'contains_file' => $containsFile,
             'active' => $active,
             'accepted' => '1',
             'qualificator_id' => 0,
@@ -4251,7 +4030,7 @@ function processWorkForm(
                 }
             }
             $workData = get_work_data_by_id($workId);
-            if ($showFlashMessage) {
+            if ($workData && $showFlashMessage) {
                 Display::addFlash(Display::return_message(get_lang('DocAdd')));
             }
         }
@@ -4972,7 +4751,6 @@ function updateSettings($courseInfo, $showScore, $studentDeleteOwnPublication)
     Session::write('_course', $courseInfo);
 
     // changing the tool setting: is a student allowed to delete his/her own document
-
     // counting the number of occurrences of this setting (if 0 => add, if 1 => update)
     $query = "SELECT * FROM $table_course_setting
               WHERE
@@ -5035,10 +4813,10 @@ function makeInvisible($item_id, $course_info)
         return false;
     }
 
-    $work_table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
+    $table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
     $item_id = intval($item_id);
     $course_id = $course_info['real_id'];
-    $sql = "UPDATE  ".$work_table."
+    $sql = "UPDATE $table
             SET accepted = 0
             WHERE c_id = $course_id AND id = '".$item_id."'";
     Database::query($sql);
@@ -5117,7 +4895,6 @@ function showStudentList($workId)
         ],
     ];
     $token = null;
-
     $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_work_student_list_overview&work_id='.$workId.'&'.api_get_cidreq();
 
     $columns = [
@@ -5356,7 +5133,6 @@ function getFileContents($id, $course_info, $sessionId = 0, $correction = false)
     }
 
     $table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
-
     if (!empty($course_info['real_id'])) {
         $sql = 'SELECT *
                 FROM '.$table.'
@@ -5533,7 +5309,6 @@ function exportAllStudentWorkFromPublication(
     }
 
     $workData = get_work_data_by_id($workId);
-
     if (empty($workData)) {
         return false;
     }
@@ -5667,9 +5442,7 @@ function downloadAllFilesPerUser($userId, $courseInfo)
 
     $tempZipFile = api_get_path(SYS_ARCHIVE_PATH).api_get_unique_id().".zip";
     $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/work/';
-
     $zip = new PclZip($tempZipFile);
-
     $workPerUser = getWorkPerUser($userId);
 
     if (!empty($workPerUser)) {

+ 2 - 2
main/work/work.php

@@ -227,7 +227,7 @@ switch ($action) {
         }
         break;
     case 'move':
-        /*	Move file form request */
+        // Move file form request
         if ($is_allowed_to_edit) {
             if (!empty($item_id)) {
                 $content = generateMoveForm(
@@ -340,7 +340,7 @@ switch ($action) {
 
         break;
     case 'list':
-        /*	Display list of student publications */
+        /* Display list of student publications */
         if (!empty($my_folder_data['description'])) {
             $content = '<div>'.
                 get_lang('Description').':'.Security::remove_XSS($my_folder_data['description'], STUDENT).

+ 92 - 17
main/work/work_list.php

@@ -129,13 +129,53 @@ if (!api_is_invitee()) {
             get_lang('Actions'),
         ];
 
-        $column_model = [
-            ['name' => 'type', 'index' => 'file', 'width' => '5', 'align' => 'left', 'search' => 'false', 'sortable' => 'false'],
-            ['name' => 'title', 'index' => 'title', 'width' => '40', 'align' => 'left', 'search' => 'false', 'wrap_cell' => 'true'],
-            ['name' => 'qualification', 'index' => 'qualification', 'width' => '30', 'align' => 'center', 'search' => 'true'],
-            ['name' => 'sent_date', 'index' => 'sent_date', 'width' => '30', 'align' => 'left', 'search' => 'true', 'wrap_cell' => 'true'],
-            ['name' => 'qualificator_id', 'index' => 'qualificator_id', 'width' => '20', 'align' => 'left', 'search' => 'true'],
-            ['name' => 'actions', 'index' => 'actions', 'width' => '20', 'align' => 'left', 'search' => 'false', 'sortable' => 'false'],
+        $columnModel = [
+            [
+                'name' => 'type',
+                'index' => 'file',
+                'width' => '5',
+                'align' => 'left',
+                'search' => 'false',
+                'sortable' => 'false',
+            ],
+            [
+                'name' => 'title',
+                'index' => 'title',
+                'width' => '40',
+                'align' => 'left',
+                'search' => 'false',
+                'wrap_cell' => 'true',
+            ],
+            [
+                'name' => 'qualification',
+                'index' => 'qualification',
+                'width' => '30',
+                'align' => 'center',
+                'search' => 'true',
+            ],
+            [
+                'name' => 'sent_date',
+                'index' => 'sent_date',
+                'width' => '30',
+                'align' => 'left',
+                'search' => 'true',
+                'wrap_cell' => 'true',
+            ],
+            [
+                'name' => 'qualificator_id',
+                'index' => 'qualificator_id',
+                'width' => '20',
+                'align' => 'left',
+                'search' => 'true',
+            ],
+            [
+                'name' => 'actions',
+                'index' => 'actions',
+                'width' => '20',
+                'align' => 'left',
+                'search' => 'false',
+                'sortable' => 'false',
+            ],
         ];
     } else {
         $type = 'complex';
@@ -148,32 +188,67 @@ if (!api_is_invitee()) {
             get_lang('Actions'),
         ];
 
-        $column_model = [
-            ['name' => 'type', 'index' => 'file', 'width' => '5', 'align' => 'left', 'search' => 'false', 'sortable' => 'false'],
-            ['name' => 'title', 'index' => 'title', 'width' => '60', 'align' => 'left', 'search' => 'false', 'wrap_cell' => "true"],
-            ['name' => 'qualification', 'index' => 'qualification', 'width' => '30', 'align' => 'center', 'search' => 'true'],
-            ['name' => 'sent_date', 'index' => 'sent_date', 'width' => '30', 'align' => 'left', 'search' => 'true', 'wrap_cell' => 'true', 'sortable' => 'false'],
-            ['name' => 'actions', 'index' => 'actions', 'width' => '20', 'align' => 'left', 'search' => 'false', 'sortable' => 'false'],
+        $columnModel = [
+            [
+                'name' => 'type',
+                'index' => 'file',
+                'width' => '5',
+                'align' => 'left',
+                'search' => 'false',
+                'sortable' => 'false',
+            ],
+            [
+                'name' => 'title',
+                'index' => 'title',
+                'width' => '60',
+                'align' => 'left',
+                'search' => 'false',
+                'wrap_cell' => "true",
+            ],
+            [
+                'name' => 'qualification',
+                'index' => 'qualification',
+                'width' => '30',
+                'align' => 'center',
+                'search' => 'true',
+            ],
+            [
+                'name' => 'sent_date',
+                'index' => 'sent_date',
+                'width' => '30',
+                'align' => 'left',
+                'search' => 'true',
+                'wrap_cell' => 'true',
+                'sortable' => 'false',
+            ],
+            [
+                'name' => 'actions',
+                'index' => 'actions',
+                'width' => '20',
+                'align' => 'left',
+                'search' => 'false',
+                'sortable' => 'false',
+            ],
         ];
     }
 
-    $extra_params = [
+    $extraParams = [
         'autowidth' => 'true',
         'height' => 'auto',
-        'sortname' => 'firstname',
+        'sortname' => 'sent_date',
+        'sortorder' => 'desc',
     ];
 
     $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_work_user_list&work_id='.$workId.'&type='.$type.'&'.api_get_cidreq();
     $content .= '
         <script>
             $(function() {
-                '.Display::grid_js('results', $url, $columns, $column_model, $extra_params).'            
+                '.Display::grid_js('results', $url, $columns, $columnModel, $extraParams).'            
             });
         </script>
     ';
 
     $content .= getAllDocumentsFromWorkToString($workId, $courseInfo);
-
     $tableWork = Display::grid_html('results');
     $content .= Display::panel($tableWork);
 }

+ 2 - 3
main/work/work_list_all.php

@@ -124,7 +124,7 @@ switch ($action) {
         exit;
         break;
     case 'make_visible':
-        /*	Visible */
+        /* Visible */
         if ($is_allowed_to_edit) {
             if (!empty($itemId)) {
                 if (isset($itemId) && $itemId == 'all') {
@@ -138,7 +138,7 @@ switch ($action) {
         }
         break;
     case 'make_invisible':
-        /*	Invisible */
+        /* Invisible */
         if (!empty($itemId)) {
             if (isset($itemId) && $itemId == 'all') {
             } else {
@@ -370,7 +370,6 @@ echo $documentsAddedInWork;
 $tableWork = Display::grid_html('results');
 
 echo workGetExtraFieldData($workId);
-
 echo Display::panel($tableWork);
 
 echo '<div class="list-work-results">';

+ 3 - 2
main/work/work_list_others.php

@@ -37,7 +37,7 @@ protectWork($courseInfo, $workId);
 $htmlHeadXtra[] = api_get_jqgrid_js();
 
 if (!empty($group_id)) {
-    $group_properties = GroupManager :: get_group_properties($group_id);
+    $group_properties = GroupManager::get_group_properties($group_id);
     $show_work = false;
 
     if (api_is_allowed_to_edit(false, true)) {
@@ -82,7 +82,8 @@ echo '<a href="'.api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq().'
 echo '</div>';
 
 if (!empty($my_folder_data['description'])) {
-    echo '<p><div><strong>'.get_lang('Description').':</strong><p>'.Security::remove_XSS($my_folder_data['description']).'</p></div></p>';
+    echo '<p><div><strong>'.get_lang('Description').':</strong><p>'.
+        Security::remove_XSS($my_folder_data['description']).'</p></div></p>';
 }
 
 $check_qualification = intval($my_folder_data['qualification']);

+ 4 - 2
main/work/work_missing.php

@@ -106,11 +106,13 @@ $output = '';
 if (!empty($workId)) {
     if (empty($_GET['list']) or Security::remove_XSS($_GET['list']) == 'with') {
         $output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&id='.$workId.'&list=without">'.
-            Display::return_icon('exercice_uncheck.png', get_lang('ViewUsersWithoutTask'), '', ICON_SIZE_MEDIUM)."</a>";
+            Display::return_icon('exercice_uncheck.png', get_lang('ViewUsersWithoutTask'), '', ICON_SIZE_MEDIUM).
+            "</a>";
     } else {
         if (!isset($_GET['action']) || (isset($_GET['action']) && $_GET['action'] != 'send_mail')) {
             $output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&id='.$workId.'&list=without&action=send_mail&sec_token='.$token.'">'.
-                Display::return_icon('mail_send.png', get_lang('ReminderMessage'), '', ICON_SIZE_MEDIUM)."</a>";
+                Display::return_icon('mail_send.png', get_lang('ReminderMessage'), '', ICON_SIZE_MEDIUM).
+                "</a>";
         } else {
             $output .= Display::return_icon('mail_send_na.png', get_lang('ReminderMessage'), '', ICON_SIZE_MEDIUM);
         }

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