Quellcode durchsuchen

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

Julio vor 8 Jahren
Ursprung
Commit
4dbfbbef4f
38 geänderte Dateien mit 836 neuen und 325 gelöschten Zeilen
  1. 1 1
      .gitignore
  2. 29 0
      app/Migrations/Schema/V110/Version20160808110200.php
  3. 48 0
      app/Migrations/Schema/V111/Version20160808154200.php
  4. 32 0
      app/Migrations/Schema/V111/Version20160808160000.php
  5. 2 2
      composer.json
  6. 176 25
      documentation/changelog.html
  7. 16 11
      main/admin/settings.lib.php
  8. 70 62
      main/exercise/answer.class.php
  9. 1 0
      main/exercise/export/aiken/aiken_classes.php
  10. 150 42
      main/exercise/export/exercise_import.inc.php
  11. 6 3
      main/exercise/export/qti2/qti2_export.php
  12. 2 1
      main/exercise/qti2.php
  13. 42 34
      main/exercise/question.class.php
  14. 22 15
      main/exercise/unique_answer.class.php
  15. 2 2
      main/inc/ajax/work.ajax.php
  16. 16 0
      main/inc/lib/api.lib.php
  17. 5 58
      main/inc/lib/banner.lib.php
  18. 7 2
      main/inc/lib/formvalidator/FormValidator.class.php
  19. 6 15
      main/inc/lib/pdf.lib.php
  20. 1 0
      main/inc/lib/system/session.class.php
  21. 2 0
      main/inc/lib/template.lib.php
  22. 1 1
      main/inc/lib/userportal.lib.php
  23. 1 0
      main/install/index.php
  24. 2 0
      main/install/install.lib.php
  25. 1 1
      main/install/version.php
  26. 4 0
      main/lang/english/trad4all.inc.php
  27. 4 0
      main/lang/french/trad4all.inc.php
  28. 4 0
      main/lang/spanish/trad4all.inc.php
  29. 2 1
      main/session/about.php
  30. 3 3
      main/template/default/learnpath/view.tpl
  31. 1 3
      main/template/default/mail/header.tpl
  32. 37 9
      main/work/work.lib.php
  33. 14 1
      plugin/bbb/README.md
  34. 66 0
      src/Chamilo/CoreBundle/Component/Utils/ChamiloApi.php
  35. 25 16
      src/Chamilo/CourseBundle/Component/CourseCopy/CourseRestorer.php
  36. 24 4
      src/Chamilo/CourseBundle/Entity/CQuizAnswer.php
  37. 10 2
      src/Chamilo/CourseBundle/Entity/CQuizQuestion.php
  38. 1 11
      tests/main/exercice/export/exercise_import.inc.test.php

+ 1 - 1
.gitignore

@@ -43,7 +43,7 @@ plugin/bbb/config.vm.php
 # Cron temp files
 main/cron/incoming/*
 
-# Stuff updated through composer
+# Stuff updated through composer - Remove just before release
 composer.lock
 vendor
 web/assets/*

+ 29 - 0
app/Migrations/Schema/V110/Version20160808110200.php

@@ -0,0 +1,29 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+namespace Application\Migrations\Schema\V110;
+
+use Application\Migrations\AbstractMigrationChamilo;
+use Doctrine\DBAL\Schema\Schema;
+
+/**
+ * Set null to post_parent_id when it is 0 on c_forum_post table
+ */
+class Version20160808110200 extends AbstractMigrationChamilo
+{
+    /**
+     * @param Schema $schema
+     */
+    public function up(Schema $schema)
+    {
+        $this->addSql("UPDATE c_forum_post SET post_parent_id = NULL WHERE post_parent_id = 0");
+    }
+
+    /**
+     * @param Schema $schema
+     */
+    public function down(Schema $schema)
+    {
+        $this->addSql('UPDATE c_forum_post SET post_parent_id = 0 WHERE post_parent_id = NULL');
+    }
+}

+ 48 - 0
app/Migrations/Schema/V111/Version20160808154200.php

@@ -0,0 +1,48 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+namespace Application\Migrations\Schema\V111;
+
+use Application\Migrations\AbstractMigrationChamilo,
+    Doctrine\DBAL\Schema\Schema;
+
+/**
+ * Class Version20160808154200
+ * Set ponderation and destination for questions and answers
+ * @package Application\Migrations\Schema\V111
+ */
+class Version20160808154200 extends AbstractMigrationChamilo
+{
+
+    /**
+     * @param Schema $schema
+     */
+    public function up(Schema $schema)
+    {
+        $question = $schema->getTable('c_quiz_question');
+        $question
+            ->getColumn('ponderation')
+            ->setDefault(0);
+
+        $answer = $schema->getTable('c_quiz_answer');
+        $answer
+            ->getColumn('ponderation')
+            ->setDefault(0);
+        $answer
+            ->getColumn('destination')
+            ->setNotnull(false)
+            ->setDefault(null);
+    }
+
+    /**
+     * @param Schema $schema
+     */
+    public function down(Schema $schema)
+    {
+        $answer = $schema->getTable('c_quiz_answer');
+        $answer
+            ->getColumn('destination')
+            ->setNotnull(true)
+            ->setDefault(0);
+    }
+}

+ 32 - 0
app/Migrations/Schema/V111/Version20160808160000.php

@@ -0,0 +1,32 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+namespace Application\Migrations\Schema\V111;
+
+use Application\Migrations\AbstractMigrationChamilo,
+    Doctrine\DBAL\Schema\Schema;
+
+/**
+ * Class Version20160808160000
+ * Set ponderation and destination for questions and answers
+ * @package Application\Migrations\Schema\V111
+ */
+class Version20160808160000 extends AbstractMigrationChamilo
+{
+
+    /**
+     * @param Schema $schema
+     */
+    public function up(Schema $schema)
+    {
+        $this->addSql("UPDATE c_quiz_answer SET destination = NULL WHERE TRIM(destination) = ''");
+    }
+
+    /**
+     * @param Schema $schema
+     */
+    public function down(Schema $schema)
+    {
+        $this->addSql("UPDATE c_quiz_answer SET destination = '' WHERE destination IS NULL");
+    }
+}

+ 2 - 2
composer.json

@@ -91,13 +91,13 @@
         "media-alchemyst/media-alchemyst": "~0.5",
         "php-ffmpeg/php-ffmpeg": "0.5.1",
         "imagine/imagine": "0.6.2",
-        "essence/essence": "^2.5",
         "jeroendesloovere/vcard": "^1.2",
         "knplabs/doctrine-behaviors": "~1.1",
         "jimmiw/php-time-ago": "^0.4.11",
         "phpoffice/phpword": "^0.12.1",
         "facebook/php-sdk-v4" : "~5.0",
-        "kigkonsult/icalcreator" : "dev-master"
+        "kigkonsult/icalcreator" : "dev-master",
+        "essence/essence": "^3.2"
     },
     "require-dev": {
         "behat/behat": "@stable",

+ 176 - 25
documentation/changelog.html

@@ -49,7 +49,7 @@
 
     <a name="1.11.0"></a>
 
-    <h1>Chamilo 1.11.0 - Lecce, 28th of July 2016</h1>
+    <h1>Chamilo 1.11.0 - Lecce, ?th of August 2016</h1>
     <h3>Release notes - summary</h3>
     <p>Chamilo 1.11.0 is a major release of the 1.11.x branch. It is meant as a transition between 1.10 and 2.0 versions.</p>
     <h3>Release name</h3>
@@ -59,54 +59,204 @@
     <h3>Possibly breaking changes</h3>
     <p>None in this version.</p>
     <h3>Notable new Features</h3>
-    <h4>For end-users, teachers and Chamilo admins</h4>
+    <h4>For end-users, teachers and Chamilo admins (by relevance)</h4>
     <ul>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a308cb81ff467d6017395ef35afcb677e09ff35b">a308cb81</a>) Add Moodle courses import feature</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a308cb81ff467d6017395ef35afcb677e09ff35b">a308cb81</a>) Add basic Moodle courses import feature</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/238763aece0cdd01a21ff50536ab656292a8ed2d">238763ae</a> - <a href="https://support.chamilo.org/issues/8307">#8307</a>) Add recalculate students score feature in exercises</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/91ab44dc37711a51094413c213bdeafc49b8303e">91ab44dc</a> - <a href="https://task.beeznest.com/issues/11321">BT#11321</a>) Add exact question export to SCORM</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/b1734ced90bb09e0f2baaa5b53cd231395f7f856">b1734ced</a> - <a href="https://task.beeznest.com/issues/11321">BT#11321</a>) Add free question in scorm export</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/ece817c5446067b9184d8c7b23e72a634cbb7d85">ece817c5</a> - <a href="https://support.chamilo.org/issues/8195">#8195</a>) Add Start/End date and validation in forum</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/dd051695d71cb7e69969d6b3644a5b1ee79bedfb">dd051695</a>) Add new exercise option allowin to show the correct answers only on the last attempt</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/77799c0025684839e1128564e63993b9fe4ae8ec">77799c00</a> - <a href="https://task.beeznest.com/issues/11438">BT#11438</a>) Add support for IMS/QTIv1.2 import</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/922cf4d46e70bbc188f9f920298005f0ce983613">922cf4d4</a> - <a href="https://task.beeznest.com/issues/11478">BT#11478</a>) Add function to prevent double ajax drag and drop upload in assignments</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a904974a3931ddbfbe2796ce6f8c5d05365bbfb1">a904974a</a> - <a href="https://task.beeznest.com/issues/10856">BT#10856</a>) Display Achieved Skills on Learner Details In Course page</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/5b4e3b655f1c39d2c069346e2b8322e5ff116734">5b4e3b65</a> - <a href="https://task.beeznest.com/issues/10856">BT#10856</a>) Show achieved badges by course in my_progress page</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/d8e18c229a3c7b345274ca6f38292b5f863636c4">d8e18c22</a>) Group Tutor can moderate forum groups</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/ad40e23ae2e7731ec4b801e6013a0ecfd0ac0981">ad40e23a</a> - <a href="https://task.beeznest.com/issues/11032">BT#11032</a>) Add teacher time report by session</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/0225a3dc1c1c4572bee3d13a8100aa42f1146a52">0225a3dc</a> - <a href="https://task.beeznest.com/issues/11031">BT#11031</a>) Add Work Report</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/39b620185f68ac86209f40917b8873a6894bebaa">39b62018</a> - <a href="https://support.chamilo.org/issues/8299">#8299</a>) Allow students to edit the wiki page and fix lock page only for trainers</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/ece817c5446067b9184d8c7b23e72a634cbb7d85">ece817c5</a> - <a href="https://support.chamilo.org/issues/8195">#8195</a>) Add Start/End date and validation in forum</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/d1503a94ec0f20b73c882984bccb92bbec62e069">d1503a94</a> - <a href="https://task.beeznest.com/issues/10651">BT#10651</a>) Add new "All Issued" page for Same skills badges obtained by a user</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/b146d02a2e8b2fe4263006b121c22c16f08825fe">b146d02a</a> - <a href="https://task.beeznest.com/issues/10651">BT#10651</a>) Add Skills Level Management</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/dd051695d71cb7e69969d6b3644a5b1ee79bedfb">dd051695</a>) Add new exercise option allowin to show the correct answers only on the last attempt</li>
-
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/91ab44dc37711a51094413c213bdeafc49b8303e">91ab44dc</a> - <a href="https://task.beeznest.com/issues/11321">BT#11321</a>) Add exact question type export to SCORM format</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/b1734ced90bb09e0f2baaa5b53cd231395f7f856">b1734ced</a> - <a href="https://task.beeznest.com/issues/11321">BT#11321</a>) Add free question type export to SCORM format</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/9b5779acf8f8fc98c5fd219b333470e96773a7c6">9b5779ac</a>) Add document size selector for teachers in Rapid PPT conversion</li>
     </ul>
-    <h4>For developers and sysadmins</h4>
+    <h4>For developers and sysadmins (by relevance)</h4>
     <ul>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/b146d02a2e8b2fe4263006b121c22c16f08825fe">b146d02a</a> - <a href="https://task.beeznest.com/issues/10651">BT#10651</a>) Add Skills Level Management</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/3562bbc2ad81c266a18e2d3d56ec130378d51244">3562bbc2</a> - <a href="https://support.chamilo.org/issues/8302">#8302</a>) Added User Tag Filter to subscribe users to sessions</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/0106766c7021f22570c433ed34bdb92dda9be765">0106766c</a>) Add webservices to add or remove visibility rules between users and courses in the courses catalogue</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/43ad39178e155ecfa066d85bcd92c5496096a36d">43ad3917</a>) Add backend feature to let choose which users can see or not see which courses in the courses catalogue</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/439008d4c5868283491e5df561614eb7dbae1b51">439008d4</a> - <a href="https://support.chamilo.org/issues/8308">#8308</a>) Add mail tester interface on admin panel</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/f2bf214175308a627d420561ed4f114e9ef1e60c">f2bf2141</a> - <a href="https://support.chamilo.org/issues/8318">#8318</a>) Add support for skills tags</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/389f1174697be95de4928dbb97cd076191d28203">389f1174</a> - <a href="https://support.chamilo.org/issues/8262">#8262</a>) Added move_uploaded_file validations when you uploading a work or correction</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/d64d55da617d8a179e44e70c6855e5fc958ff3b9">d64d55da</a> - <a href="https://task.beeznest.com/issues/10651">BT#10651</a>) Allow student boss to assign badges to users</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/4af52bf917eb7949da80fef0e972bc635c8d7a3f">4af52bf9</a>) Add sso_authentication_subclass setting</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/4898060d2f1629e22a0f7d94f982ddab2499c6fa">4898060d</a>) Add Geolocalization plugin to user edition page</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/0b5590081e2f8b708bcce6864d27ea622c727e28">0b559008</a> - <a href="https://support.chamilo.org/issues/8364">#8364</a>) Added Badge studio embedded editor</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/f9222e16bc01e1ff014fd6c42b5460ec91f0f31c">f9222e16</a>) Add option to send email to all admins that a new user is registered. (option turned off)</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/c884313fd763c596fa60d35e42d458c69231cbbb">c884313f</a> - <a href="https://task.beeznest.com/issues/10956">BT#10956</a>) Add Skype and LinkedIn extra fields to remove Skype plugin</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/81a49696dd7583253fd8c83d80cb62de24a3d042">81a49696</a> - <a href="https://support.chamilo.org/issues/8261">#8261</a>) New region plugin in the administration screen</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/b57a56fd36b863839874b4ad283b3bb9cc247858">b57a56fd</a> - <a href="https://task.beeznest.com/issues/11157">BT#11157</a>) Add global video conference link in menu</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/81a49696dd7583253fd8c83d80cb62de24a3d042">81a49696</a> - <a href="https://support.chamilo.org/issues/8261">#8261</a>) New plugin region in the administration screen</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/21e47bca07b36062ef53af7132005f5b777a26b0">21e47bca</a>) Refactoring: move main/newscorm/ to main/lp/ and related folders (except code for migration from 1.9 and 1.10)</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/ba818ee23e39cf37f4c79c7aedd10b7f5a39262f">ba818ee2</a>) Refactoring: move main/exercice/ to main/exercise/ and related folders (except code for migration from 1.9 and 1.10)</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/87d5408689c5a4c1c11a6dbdc0494126b05f411b">87d54086</a> - <a href="https://support.chamilo.org/issues/10992">#10992</a>) Add Geolocalization User Extra Field Type</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/923d872edc8ea777368087498ec4e1d85f47b21e">923d872e</a>) Add buyCourse plugin integration to the new sessions grid catalog</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/b57a56fd36b863839874b4ad283b3bb9cc247858">b57a56fd</a> - <a href="https://task.beeznest.com/issues/11157">BT#11157</a>) Add global video conference link in menu</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/923d872edc8ea777368087498ec4e1d85f47b21e">923d872e</a>) Add buyCourse plugin integration to the new sessions "grid" catalogue</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/43210addda08d810c18f51d4ced902e3c4718cfc">43210add</a> - <a href="https://task.beeznest.com/issues/11015">BT#11015</a>) Add translations support to extra fields</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/1bc176b99ac5a7ab34e12cff6b0075fc9421e32a">1bc176b9</a> - <a href="https://support.chamilo.org/issues/8261">#8261</a>) New region plugin in the administration screen</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/c1c0ee9cb4a296bf628aec021ccdc8743b769e29">c1c0ee9c</a> - <a href="https://task.beeznest.com/issues/11214">BT#11214</a>) Add Azure Active Directory plugin</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/b58c7ac0ca4ae0ceb7af29b16d0f988ad0f7903a">b58c7ac0</a> - <a href="https://task.beeznest.com/issues/11081">BT#11081</a>) Move ticket from plugin to Chamilo core tool</li>
-        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/d85d8d551b698d1062062b5c63e08c5c7dc7296d">d85d8d55</a> - <a href="https://support.chamilo.org/issues/8170">#8170</a>) Added Course Category Name to a export/import list courses by CSV</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/d85d8d551b698d1062062b5c63e08c5c7dc7296d">d85d8d55</a> - <a href="https://support.chamilo.org/issues/8170">#8170</a>) Add Course Category Name to a export/import list courses by CSV</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/851ca9c84fbfd09f2fe81d1bc87b444384494f8d">851ca9c8</a> - <a href="https://task.beeznest.com/issues/11080">BT#11080</a>) Course plugins can now show information in regions</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/c213a51f83f5f7f970ca93fc42edb70148ea9195">c213a51f</a> - <a href="https://task.beeznest.com/issues/10651">BT#10651</a>) Add manual assignment of badges to users (by admin user)</li>
-
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/d64d55da617d8a179e44e70c6855e5fc958ff3b9">d64d55da</a> - <a href="https://task.beeznest.com/issues/10651">BT#10651</a>) Allow student boss to assign badges to users</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/9182aa0b400f26721ce97c649317f142247fba9f">9182aa0b</a>) Add vchamilo virtualization plugin (Chamilo instances management)</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/9182aa0b400f26721ce97c649317f142247fba9f">9182aa0b</a>) Add vchamilo import to import normal Chamilo installations into an instance model</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/46cc928740ceedb41e0b0382e0edaadc84335f78">46cc9287</a>) Add vchamilo upgrade instance option</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/5562832a4cc600f7c8280daab008ab625cd2f9a5">5562832a</a>) Add global videoconference to BBB plugin</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/43ad39178e155ecfa066d85bcd92c5496096a36d">43ad3917</a>) Add backend feature to let choose which users can see which courses in the courses catalogue</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/0106766c7021f22570c433ed34bdb92dda9be765">0106766c</a>) Add webservices to add or remove visibility rules between users and courses in the courses catalogue</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/439008d4c5868283491e5df561614eb7dbae1b51">439008d4</a> - <a href="https://support.chamilo.org/issues/8308">#8308</a>) Add mail tester interface on admin panel</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/389f1174697be95de4928dbb97cd076191d28203">389f1174</a> - <a href="https://support.chamilo.org/issues/8262">#8262</a>) Added move_uploaded_file validations when you uploading a work or correction</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/4af52bf917eb7949da80fef0e972bc635c8d7a3f">4af52bf9</a>) Add sso_authentication_subclass setting</li>
     </ul>
-    <h3>Improvements (minor features) and debug</h3>
+    <h3>Improvements (minor features) and debug (by date - the latest are listed first)</h3>
     <ul>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/080258d2a80a9f1f29d852a238f122eafa2f6c64">080258d2</a> - <a href="https://task.beeznest.com/issues/11477">BT#11477</a>) Fix migration when moving files</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/52dcdc532fbd9f3d2105795c4bcb1cf1276f2aed">52dcdc53</a> - <a href="https://task.beeznest.com/issues/11477">BT#11477</a>) Fix Migration on c_item_property</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/b13725313412ec8f540de61dd85f8b2c84f82f51">b1372531</a> - <a href="https://task.beeznest.com/issues/11438">BT#11438</a>) Add comments and validations to IMS/QTI import code</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/8a184a79d7450e8d9a29f22b2c64fc50d74b59ec">8a184a79</a> - <a href="https://task.beeznest.com/issues/11438">BT#11438</a>) Add (incomplete) support for attached media in IMS/QTI v1.2 import</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/c6730139827f1a0953a48cd67e4c3def41f3993e">c6730139</a>) Set doctype html5 for system document templates</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/7ba2774af998befbc1c2a81a31eb292f164af846">7ba2774a</a> - <a href="https://task.beeznest.com/issues/11478">BT#11478</a>) Fix "-" if qualification doesn't exist in last work upload + added upload success msg</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/110e833e50d0a482862655b9d50154286bbf1ae8">110e833e</a>) Fix export to PDF from HTML docs</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/2919a29e87b42e34a451faa943ee574680cc7c64">2919a29e</a>) Fix reduced footer issues in learning paths</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/de40643fd31985d719a981c05bf46fed1e34a0a7">de40643f</a> - <a href="https://task.beeznest.com/issues/11438">BT#11438</a>) Fix IMS/QTI v2.0 import</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/840f49cfbd2e656bcf1d265d51d02caaeabf56fc">840f49cf</a> - <a href="https://task.beeznest.com/issues/11438">BT#11438</a>) Add support for two-levels zips for IMS/QTI v1.2 import</li>
+
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/46c7afa543087587d852dd098bf1f815bf0bfda9">46c7afa5</a>) Use DOMDocument to add print button for certificates</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/b78b3c9df61de5951df27732496d6000ca327943">b78b3c9d</a>) Remove Student/Teacher view from places that is not in the course and it tools</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/2025ff53fa690fb5480549f5f8956383ca6dbc4c">2025ff53</a> - <a href="https://support.chamilo.org/issues/8374">#8374</a>) Update Wiris doc in installation guide</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/92bb6158ad9cd679576c42e7d31826ff8908b4c8">92bb6158</a> - <a href="https://support.chamilo.org/issues/8334">#8334</a>) Fix match question type edit error</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/b5a0a701e075b6e9b3b0737d037c2210072aabe8">b5a0a701</a> - <a href="https://task.beeznest.com/issues/11455">BT#11455</a>) Fix learning paths sidebar scrolling</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/08a0d41a95f286ddf2e5df30e216d338c82cb833">08a0d41a</a>) Change lang variable in badge studio</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/068e786fe64c683253fc59492f77172fe16367ec">068e786f</a> - <a href="https://support.chamilo.org/issues/8364">#8364</a>) Fix save badge button and add button to set the badge to the form</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/c1b15239ac1bac714638aa006bb3271e0b2f071f">c1b15239</a>) Set gray color to disabled skills in skill wheel</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/4c4b847174f6930107ba737811282fc78601d422">4c4b8471</a>) Fix namespace, fix PHP warning</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/b75d2fa30ca78d37d2ea9b3ee295c28adc3c9df4">b75d2fa3</a>) Fix indexes in CLp entity</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/23ca116ab6b0ca4eed5f972e25fb975874a6cc4e">23ca116a</a>) Fix lp scorm export to PDF (no images)</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/24411bf33df074611a23b345b91376a4dce7e768">24411bf3</a>) Format code fix validation.</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/559b244bf27f8d60f636f7084c36deb56d42cbc3">559b244b</a>) Remove api_html_entity_decode and stripslashes</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/1df72cd51b4fa35e9d327cd94c9705347340a643">1df72cd5</a>) Add autocapitalize = none</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/d542df949bb9b8fd736c3bc1e31efef0f090c77a">d542df94</a> - <a href="https://task.beeznest.com/issues/11447">BT#11447</a>) Fix more migration issues</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/6c5445b27b688304026657751cac748ca8e71e30">6c5445b2</a> - <a href="https://task.beeznest.com/issues/11447">BT#11447</a>) Fix more migration issues</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/503274de44aae5b72b22b4ae725f09d294259c55">503274de</a>) Fix scorm import, fix bug that replace "_" with "-" when using urlify</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/6066fb86be967d94841724f0a8a2076253b6dc99">6066fb86</a>) Swap Skill Wheel colors (green/blue)</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/033bfd98b3db82a8888e69cb05ba2707bc8e7c9a">033bfd98</a>) Add initial branch_sync row for empty installs</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/56882c943395947305b1773150fc3ec77a92c964">56882c94</a> - <a href="https://support.chamilo.org/issues/8331">#8331</a>) Fix courses tables for migration and imports</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/5cc1a1b8f6314dcf2d74b2a870d7cb55a9fe7271">5cc1a1b8</a> - <a href="https://support.chamilo.org/issues/8161">#8161</a>) Improved view of groups in the social network</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/fde6c7ccb4370919b131bf56e220dd8aa2cca200">fde6c7cc</a>) Update readme.md to add section on how to run Behat tests</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/34397791f6b5adfac5bf371c2cd90be33a3de264">34397791</a> - <a href="https://support.chamilo.org/issues/8370">#8370</a>) Fix skill parent "None" that shows if a root skill already exists</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/f3093f40eb4340ece5fb644fc46213c1400ac35a">f3093f40</a>) Replace PEAR::raiseError with new Exception</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/3262a0fc6af5dbd22633416b77f193de3f569855">3262a0fc</a>) Fix query.</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/89524d42109dbadc624fd1ceba46db1f9bdbca30">89524d42</a> - <a href="https://task.beeznest.com/issues/7685">BT#7685</a>) Close div tags in installer</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/d17b4d4c1a73a46bc7b873f989a18aae28f537fd">d17b4d4c</a>) Add validations to avoid NOTICE messages if profile.conf.php does not exist (and as such its constants are not available)</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/bcea8b3046371a0c767183ad13866ecc899e01e9">bcea8b30</a>) Change installation complete button position to fix issue on mobile devices</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a41929ac42be9ed62c0aa2b30e6970f4a10b04c7">a41929ac</a> - <a href="https://support.chamilo.org/issues/8336">#8336</a>) Fix Draggable matching answer</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/05994edbfb44f1e0cbbce2d2627cc876e0060780">05994edb</a> - <a href="https://support.chamilo.org/issues/8365">#8365</a>) Fix translation on BBB plugin</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/6b29f9c9ff0b6bf34267cf1a6fdbdf0928e6949c">6b29f9c9</a> - <a href="https://support.chamilo.org/issues/8365">#8365</a>) Fix BBB api</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/78dc19ba40c9ba569dd53207e47de061c9b471cc">78dc19ba</a> - <a href="https://task.beeznest.com/issues/11419">BT#11419</a>) Set flash_flvPlayer as rel path</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/7d175d49a0782f77add1974c176bf1fb2693b815">7d175d49</a>) Fix video plugin view</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/08a54874ed506d8a50ca7c0c161181947487eb20">08a54874</a> - <a href="https://task.beeznest.com/issues/11431">BT#11431</a>) Fix Firefox v47 error if poster="" </li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/97b9a5e10c20231763ed97adbbe14389a47a6333">97b9a5e1</a>) Rename function name</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/06145ce0d82e08e31c141ae1147da949fa630aa0">06145ce0</a>) Fix query</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/f9e6bc0606d5e3bc40f23a9af871d33864c132bb">f9e6bc06</a> - <a href="https://task.beeznest.com/issues/8347">BT#8347</a>) LastName and Firstname are mandatory</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/743c97a508628c1444d8da55c82f15f5ef2fdf28">743c97a5</a>) Add plugin/ticket/ to the list of deprecated directories that should be removed on install</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/1d25a7dbaa1b59655d1aabd847ba553a08cd6f75">1d25a7db</a>) Arrangement of buttons in light courses sessions</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/98450dfe3a4ed8d64fb010c954661e5bc3d6b426">98450dfe</a> - <a href="https://support.chamilo.org/issues/8340">#8340</a>) Fix prevent multiple simultaneous login</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/7d4f0ac11bdb401ed3ee59719c07e78098f668d2">7d4f0ac1</a>) Fix my_courses_view_by_session view in user_portal</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/4eb24d15bbbc28772af05fbda5b00457b3d33fc3">4eb24d15</a>) Fix quick-tool bars for course tools in two columns view</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/898c85b4edeeab127d20e1e33a9decf77cfdc7a3">898c85b4</a>) Improve CSS style for select answers in matching question</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/9416e1ce05a94850bc88f821f4b465d3a8fec262">9416e1ce</a>) Fix Behat tests</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/f58be9b55cf991b2795055af5254b646f2b5ec4c">f58be9b5</a>) Fix social invitations</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/6479e46fff36b0ff3ef810554b0b0a04c92a041a">6479e46f</a>) Add missing icons in vertical column tool view Course</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/8e80f98d31cfc760733791bbcba407583d9ad279">8e80f98d</a>) Fix include footer.js.tpl</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/e278017851f996168eaf30069767d62e7e9a687a">e2780178</a>) Fix Behat tests</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/284ee8af12476dc39d549c2dda6b489186a69619">284ee8af</a>) Fix message when forum category is added</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/f78304a26dc74375b277c466a1a1d75032ff5a3f">f78304a2</a>) Fix icon that enables multi urls portals options</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/8ee6a6449f2eb2b3dfa5f2f8a25d0748e04db02a">8ee6a644</a>) Fix icon that enables multi urls portals options</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/373991b9b8a1a3ff6ff0642116d3e115a4d02880">373991b9</a>) Replace jQuery UI accordion with Bootstrap collapse in inforcours.php</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a5af227fc05de3c847a4aaf5b0d0fb162977ca90">a5af227f</a> - <a href="https://task.beeznest.com/issues/11437">BT#11437</a>) Fix cidreq breadcrumb issue</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/2e9a3304ef24c2fdf409da380af6ab461b938773">2e9a3304</a> - <a href="https://support.chamilo.org/issues/8323">#8323</a>) Fix new document template missing list from learning paths</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/4a6e788589343c2dc37e8227cc21e4e770f763b0">4a6e7885</a>) Remove visio code, replaced by plugins (bbb,  openmeetings)</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a2fc8cecaf9d1a85912a2e9ce78a0549aea1f386">a2fc8cec</a>) Remove visio code, replaced by bbb and openmeetings</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/d9d1869ce299480b4918b2be11243baa249c3762">d9d1869c</a>) WIP ChamiloApi adding "ChamiloApi::getConfigurationValue()" function</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a3279f92173087710ae18c9e2933b2c01b92a832">a3279f92</a>) Adding ChamiloApi class to prepare the migration of methods from api.lib.php</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/966e07fce090481eb78e6c5d838667f744379728">966e07fc</a>) Remove unicity criteria for Email_canonical (had been set in previous 1.11 developments)</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/d4f7a9cda6a67d36ec1dd8fc5817611468a28990">d4f7a9cd</a> - <a href="https://support.chamilo.org/issues/8308">#8308</a>) Update e-mail tester script to only show if the test directory is there (representative of a development platform or platform managed by a developer). Add documentation</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/05835c5861c7e631639ab55ea644074a62a8c601">05835c58</a> - <a href="https://support.chamilo.org/issues/8302">#8302</a>) Use field display text instead of internal value in add users to course and session</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/bac9946d813a5b7f233e404994affc6fc9d2c078">bac9946d</a>) Fixing Behat context</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/46a8e9656c62f2951cc06c23f8ad21fe4a807b81">46a8e965</a>) Setting button view student lessons</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/7b6d08e9cc2c66c9577f699c310a0c62384699a3">7b6d08e9</a> - <a href="https://support.chamilo.org/issues/8360">#8360</a>) Show header and footer in a final item of scorm in case of error</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/2678b3c74f66726ba586b1f44d240d501b10e52e">2678b3c7</a>) Set style for attachments in new mail message</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a6ef2d55ab3c2ac33d66cc2344ae88af709caed3">a6ef2d55</a>) Fix height in multi selector ajax</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/997fa37c4acbbc3c85e67ef0e95fc22ea59d6f65">997fa37c</a>) Fix conflict between ExtraField model and ExtraField entity</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/5fab733a5f830b2f5f6c6552c1b483b7440209a1">5fab733a</a>) Fix height of input search results in table exercises</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/8853e37c493920baeddc11767d3b29d8d326cf3d">8853e37c</a>) Fix warning and notice and hide skill tag filter if there is no tags configured</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/17f05eb83b8eb7fab479d4969c9398e22035b5e2">17f05eb8</a>) Add missing footer.js.tpl</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a14c022bfe7e63f68b19e71e168c603f7ea32d50">a14c022b</a>) Remove sniff.tpl</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a15e7bbb387f94a98448b64a3cda6da9ba6ba88a">a15e7bbb</a>) Add oembed</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/ef23e1d28fb5ec94ae3211e8bc83c98f5ca5aaaa">ef23e1d2</a>) Format code, adding api_get_cidreq + fix documentation</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/c947d71e8f19555f738c9ee5b9574922869d8778">c947d71e</a>) Remove require_once 'scorm.class.php';</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/52e8df794d51af0cd097bb9ec9e46d4564760867">52e8df79</a>) Fix form + format code</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/bba57f8888e8bb6a89b51e288d9e18b66bcfeae6">bba57f88</a>) Fix function names in api.lib.php</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/208bcb14a14c6077a2d65a3b9573d2a7281fa999">208bcb14</a>) Remove Session::read('message'); use Display::addFlash, remove api_set_failure/api_get_failure</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/917ab67fdbd2df3ffd9411cd6b2900f5659aff6c">917ab67f</a>) Remove Session::read('message'); use Display::addFlash</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/b5158a117b9127c26707f63953a3c49faa4730bf">b5158a11</a>) Fix api_not_allowed UI</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/fad270e90eadd8f2498b1c6e10449642ab1c2067">fad270e9</a>) Fix namespaces</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/e1a3c19d8447cbc2a986ee3647d13a2651802f9c">e1a3c19d</a>) Change dependencies version to support 5.4</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/3a3952389c7a1e70cd613513f698cec07292652f">3a395238</a>) Use function to get all document conversion sizes (used in PPT conversion)</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/6c022643afbcfa94d40fc6606b2aba80a6a73aeb">6c022643</a>) Add function to get all document conversion sizes (used in PPT conversion)</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/bf463ee43cb768bab51df6125918c317798be6b5">bf463ee4</a>) Add README to the webservices folder</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/1ec71b08bb79ff7d85372557cdfd4fe22c17af7e">1ec71b08</a> - <a href="https://support.chamilo.org/issues/8211">#8211</a>) Fix missing statement getting the accumulate_scorm_time setting from the c_lp table</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/09f5f59cb2c28157fe556025a84f3f9afb2df950">09f5f59c</a>) Add format management to remote Rapid PPT Converter</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/9fce56b5cddf14647cd6c389fc1442dc1836e55a">9fce56b5</a>) Add format options to Rapid PPT Converter</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/99b68ac1d9ec5f3b790589b8194a91291f18252b">99b68ac1</a>) Show special courses on top in user_portal.php</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/735dac1474066a61844f85e1ff885018b702c5ab">735dac14</a>) Remove deprecated URL param</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/3619ac89169a2d81fb42e9cbe6c409b5e235f34a">3619ac89</a>) Add zip as requirement needed by phpexcel export</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/6dc4840f70c121d1122e46fbf0635bfc534399f1">6dc4840f</a>) Installing getid3 with composer</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/f9886fbb8d9de0118c62a9750f6cae617d07ff0c">f9886fbb</a>) Remove iCalcreator.class.php add lib with composer</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/5a2d3258ab27f298a2be0f8c6bf586acb459ad30">5a2d3258</a>) Allow X-Frame-Options: SAMEORIGIN in learning paths</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/4bda1d008d6d6682fd6f433f958cdc2cd097f57e">4bda1d00</a> - <a href="https://task.beeznest.com/issues/11421">BT#11421</a>) Fix bug when filtering courses</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/c719802364d4d7b51dfb1a77828c3c8390f4abdf">c7198023</a> - <a href="https://support.chamilo.org/issues/8161">#8161</a>) Added visual tweaks to social groups</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a9f7a621456f07b36bfc3a70616289572c4ab9ad">a9f7a621</a> - <a href="https://support.chamilo.org/issues/8349">#8349</a>) Fix issue with Group announcement where one could not add users</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/f9297f1c6aa3aea0328ff5445cabb3af34eb4434">f9297f1c</a> - <a href="https://support.chamilo.org/issues/8331">#8331</a>) Add warning message for new directories name when upgrading</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/6688c098ddf7a90ebc80c4379217bd0e5a6c6b53">6688c098</a>) Fix post visualization in forum threads</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/35ae690eabd1236fdf31b4f548f88ff13855ba73">35ae690e</a>) Fix issue preventing user image upload when initial image is empty - v2</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/92eb58bbebbed43f1080d4b1b155c2f97f95bd2b">92eb58bb</a> - <a href="https://support.chamilo.org/issues/8331">#8331</a>) Remove newscorm directory after migration</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/2d2df81c398a4f361b693d5860faae452dd5856e">2d2df81c</a> - <a href="https://support.chamilo.org/issues/8331">#8331</a>) Fix other migration issue with c_item_property</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/dc756b39384550336042af4d012ec01a31154465">dc756b39</a> - <a href="https://support.chamilo.org/issues/8331">#8331</a>) Fix migration process</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/71592f15266984cd22d1ed012c01e0ddabf640b4">71592f15</a> - <a href="https://support.chamilo.org/issues/8331">#8331</a>) Fix migration issue with c_item_property</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/064c945c9fb01326fc350095f92a1b8f17d1fd69">064c945c</a>) Add confirmation, bigger icons, fix warning</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/633a16118eb2d0266f050ffa52602c87038d8dc6">633a1611</a> - <a href="https://task.beeznest.com/issues/11031">BT#11031</a>) Fixing time spent on LMS - better integration</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/ae43d264fb4a01fbe76ebe4dfdd447a998af9ecc">ae43d264</a> - <a href="https://task.beeznest.com/issues/11031">BT#11031</a>) Fixing time spent on LMS - better integration</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/bb8c4bc57efbfbde92b2197e7b3c5b13c182c095">bb8c4bc5</a> - <a href="https://task.beeznest.com/issues/11031">BT#11031</a>) Fixing time spent on LMS to set it from the beginning and not only last 7 days</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/99d66c55dfb0feacc190983b4acccdd7a2957d6f">99d66c55</a> - <a href="https://task.beeznest.com/issues/11031">BT#11031</a>) Fixing time spent on LMS to set it from the beginning and not only last 7 days</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/d17a0c3aca350897e23d6deb33a51a3e891429ae">d17a0c3a</a>) Change params order returnFontAwesomeIcon</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/8348130888b77aada5e5db6772821a528fb0533a">83481308</a> - <a href="https://support.chamilo.org/issues/8348">#8348</a>) Fix show header and footer to show lessons iframe</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/dd4cc44c350952e720bc7c73a138dea6f6d08573">dd4cc44c</a>) Fix Email condition tester</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/aaee4e834731d23bbca805244767da7e9d6d8b43">aaee4e83</a>) Fix issue preventing user image upload when initial image is empty</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/734da3a120091261d68d5e907119eb5313e1f7ef">734da3a1</a>) Improved design invitations from friends on social network</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/9595138f8396d983d7db42b38abc3178cc558d73">9595138f</a>) Fix aspect ratio for user image</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/8047eb2440a951ac98a95cc5533b5bb0ad9bdd88">8047eb24</a> - <a href="https://support.chamilo.org/issues/8331">#8331</a>) Fix restore learnpath with item type = dokeos_chapter</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/357055060ccb830815d60be9936a2e2352dc59ea">35705506</a> - <a href="https://support.chamilo.org/issues/8350">#8350</a>) Fix accept friend invitation in shared profile</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/0c0bb08608a11d98af1280cc3e4f8b318a921f91">0c0bb086</a>) Fix migrations from 1.9 to 1.11</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/cc2494593ed10902c9760b219fbcbba4f7e57f07">cc249459</a>) Fix class extends</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/77e726cb3d451d377ed870bc1b4800088b6e1fae">77e726cb</a>) Add V110 migrations</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/af7f65b1710a2f362a38aa132e275fff9bbf5a6e">af7f65b1</a>) Fix table creation.</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/5809e1bd5e344e36b42974024fcdebde741830a8">5809e1bd</a>) Fix clean_up_files_in_zip function</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/415ca7c7aa9e18cc5b1f0ea4c9d73583bc5fb926">415ca7c7</a>) Blocking app/console</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/78ff5e010d6d0891e7fb8f8ecdc1cf2acc764268">78ff5e01</a>) Add 1.9.x support in the upgrade process</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/18835c4fa32372a1eb0b7e81c6555a402cd44100">18835c4f</a>) Fix issue preventing the ratio to be set on the image cropper (caused by commit af164930aa)</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/1fc7546107e4d1dd9a8e38644678484bd74aedbe">1fc75461</a>) Save c_student_publication with session = null instead of session = 0</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/8b4da8e73a0a9915b473f8f4ad6571e2ce3eb1ec">8b4da8e7</a>) Change of appearance of widget for pending user invitations</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/554c8061c4f26decc5fc773ed5fa6a478f6ee8a7">554c8061</a>) Fix issue with production files path and friends invitations image path in shared profile</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/8625503f7b181aded849b87d7657010714bc438a">8625503f</a> - <a href="https://task.beeznest.com/issues/11356">BT#11356</a>) Fix save forum threads when the forum is moderated</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/52ef413e2719be2da521beb83a476d91468ef5e7">52ef413e</a>) Add database::escape_string, Security::remove_XSS</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/0df4479ece6e50a38381f47058973e1524adc2f9">0df4479e</a> - <a href="https://support.chamilo.org/issues/8346">#8346</a>) Fix issue #8346</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/9d9afda55f606e272d592509a888d1596aac5e80">9d9afda5</a> - <a href="https://support.chamilo.org/issues/8346">#8346</a>) Fix table id</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/1fe2caca580b4a48f17902dc2887107900fb793d">1fe2caca</a> - <a href="https://support.chamilo.org/issues/8337">#8337</a>) Fix issue #8337 vars</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/3fcd83e118e40a0e5d4bb914fec937f50e0b9ffe">3fcd83e1</a>) Improve the LP action menu</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/23fb113481501862aef84697386c3c000dd57905">23fb1134</a>) Avoid repeat code to expand/contract sidebar on documents tool</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/91e7907dd334b8cb3151966a557cf7d671b13968">91e7907d</a>) Add Claroline changelog + remove dead links to old D0keos wiki (now private or disappeared)</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/c0e8ff207b0f29799b0b8687781bf9c54e99ecbe">c0e8ff20</a> - <a href="https://task.beeznest.com/issues/11395">BT#11395</a>) Fix get document list in tree</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/4b5689eae06b1082554844c30e08d567246d0d1c">4b5689ea</a> - <a href="https://task.beeznest.com/issues/11395">BT#11395</a>) Add association mapping between Session and CStudentPublication</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/63604f24a59c9c104bf433655777387c2d29df7c">63604f24</a>) Add Claroline changelog + remove dead links to old D0keos wiki (now private or disappeared)</li>
@@ -451,6 +601,8 @@
     <ul>
         <li>The main/exercice/ folder has been renamed main/exercise/</li>
         <li>The main/newscorm/ folder has been renamed main/lp/ (for Learning Path)</li>
+        <li>The plugin/ticket/ folder has been removed (and the code integrated to core)</li>
+        <li>The plugin/skype/ folder has been removed (and the code integrated to core)</li>
     </ul>
     <h3>Web services</h3>
     <ul>
@@ -464,7 +616,6 @@
     </ul>
     <h3>Known issues</h3>
     <ul>
-        <li>IMS/QTI import/export has issues</li>
         <li>In Internet Explorer 9, learning paths might not show other pages than the first to be opened. We lacked computers with IE9 at the time of release of 1.10.4. Guaranteeing IE9 support will likely require additional services. See <a href="https://support.chamilo.org/issues/8075">the corresponding bug report</a></li>
         <li>The text-to-speech converter based on Google now requires an API key</li>
         <li>The Xapian search module is broken. We'll fix that soon, but not a lot of people use it</li>

+ 16 - 11
main/admin/settings.lib.php

@@ -2,6 +2,7 @@
 /* For licensing terms, see /license.txt */
 
 use Symfony\Component\Filesystem\Filesystem;
+use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
 
 /**
  * Library of the settings.php file
@@ -372,17 +373,21 @@ function handleStylesheets()
         'settings.php?category=Stylesheets#tabs-2'
     );
 
-    $logoForm->addHtml(Display::return_message(sprintf(get_lang('TheLogoMustBeSizeXAndFormatY'), '250 x 70', 'PNG'), 'info'));
+    $logoForm->addHtml(
+        Display::return_message(sprintf(get_lang('TheLogoMustBeSizeXAndFormatY'), '250 x 70', 'PNG'), 'info')
+    );
 
     $dir = api_get_path(SYS_PUBLIC_PATH).'css/themes/' . $selected . '/images/';
     $url = api_get_path(WEB_CSS_PATH).'themes/' . $selected . '/images/';
     $logoFileName = 'header-logo.png';
-    $newLogoFileName = 'header-logo-custom.png';
+    $newLogoFileName = 'header-logo-custom' . api_get_current_access_url_id() . '.png';
+    $webPlatformLogoPath = ChamiloApi::getWebPlatformLogoPath();
 
-    if (is_file($dir.$newLogoFileName)) {
-        $logoForm->addLabel(get_lang('CurrentLogo'), '<img id="header-logo-custom" src="'. $url . $newLogoFileName .'?'. time() . '">');
-    } else {
-        $logoForm->addLabel(get_lang('CurrentLogo'), '<img id="header-logo-custom" src="'. $url . $logoFileName .'?'. time() . '">');
+    if ($webPlatformLogoPath !== null) {
+        $logoForm->addLabel(
+            get_lang('CurrentLogo'),
+            '<img id="header-logo-custom" src="' . $webPlatformLogoPath . '?' . time() . '">'
+        );
     }
 
     $logoForm->addFile('new_logo', get_lang('UpdateLogo'));
@@ -391,7 +396,7 @@ function handleStylesheets()
     if (isset($_POST['logo_reset'])) {
         if (is_file($dir.$newLogoFileName)) {
             unlink($dir.$newLogoFileName);
-            Display::display_normal_message(get_lang('ResetToTheOriginalLogo'));
+            echo Display::return_message(get_lang('ResetToTheOriginalLogo'));
             echo '<script>'
                 . '$("#header-logo").attr("src","'.$url.$logoFileName.'");'
             . '</script>';
@@ -414,15 +419,15 @@ function handleStylesheets()
                 $status = move_uploaded_file($_FILES['new_logo']['tmp_name'], $dir.$newLogoFileName);
 
                 if ($status) {
-                    Display::display_normal_message(get_lang('NewLogoUpdated'));
+                    echo Display::return_message(get_lang('NewLogoUpdated'));
                     echo '<script>'
                             . '$("#header-logo").attr("src","'.$url.$newLogoFileName.'");'
                         . '</script>';
                 } else {
-                    Display::display_error_message('Error - '.get_lang('UplNoFileUploaded'));
+                    echo Display::return_message('Error - '.get_lang('UplNoFileUploaded'), 'error');
                 }
             } else {
-                Display::display_error_message('Error - '.get_lang('InvalidImageDimensions'));
+                Display::return_message('Error - '.get_lang('InvalidImageDimensions'), 'error');
             }
         }
     }
@@ -451,7 +456,7 @@ function handleStylesheets()
             </script>';
             echo Display::tabs(
                 array(get_lang('Update'),get_lang('UpdateLogo'), get_lang('UploadNewStylesheet')),
-                array($form_change->return_form(), $logoForm->return_form(), $form->return_form())
+                array($form_change->return_form(), $logoForm->returnForm(), $form->returnForm())
             );
         } else {
             $form_change->display();

+ 70 - 62
main/exercise/answer.class.php

@@ -579,20 +579,22 @@ class Answer
         $hotspot_coordinates,
         $hotspot_type
     ) {
-        $answerTable = Database :: get_course_table(TABLE_QUIZ_ANSWER);
-
-        $params = [
-            'answer' => $answer,
-            'comment' => $comment,
-            'correct' => intval($correct),
-            'ponderation' => $weighting,
-            'position' => $position,
-            'destination' => $destination,
-            'hotspot_coordinates' => $hotspot_coordinates,
-            'hotspot_type' => $hotspot_type
-        ];
-
-        Database::update($answerTable, $params, ['iid = ?' => intval($iid)]);
+        $em = Database::getManager();
+
+        /** @var \Chamilo\CourseBundle\Entity\CQuizAnswer $quizAnswer */
+        $quizAnswer = $em->find('ChamiloCourseBundle:CQuizAnswer', $iid);
+        $quizAnswer
+            ->setAnswer($answer)
+            ->setComment($comment)
+            ->setCorrect($correct)
+            ->setPonderation($weighting)
+            ->setPosition($position)
+            ->setDestination($destination)
+            ->setHotspotCoordinates($hotspot_coordinates)
+            ->setHotspotType($hotspot_type);
+
+        $em->merge($quizAnswer);
+        $em->flush();
 	}
 
 	/**
@@ -603,6 +605,7 @@ class Answer
     public function save()
     {
 		$answerTable = Database::get_course_table(TABLE_QUIZ_ANSWER);
+        $em = Database::getManager();
 		$questionId = intval($this->questionId);
 
 		$c_id = $this->course['real_id'];
@@ -622,23 +625,32 @@ class Answer
             $iid = isset($this->iid[$i]) ? $this->iid[$i] : 0;
 
             if (!isset($this->position[$i])) {
-                $params = [
-                    'id_auto' => $autoId,
-                    'c_id' => $c_id,
-                    'question_id' => $questionId,
-                    'answer' => $answer,
-                    'correct' => intval($correct),
-                    'comment' => $comment,
-                    'ponderation' => $weighting,
-                    'position' => $position,
-                    'hotspot_coordinates' => $hotspot_coordinates,
-                    'hotspot_type' => $hotspot_type,
-                    'destination' => $destination
-                ];
-                $iid = Database::insert($answerTable, $params);
+                $quizAnswer = new \Chamilo\CourseBundle\Entity\CQuizAnswer();
+                $quizAnswer
+                    ->setIdAuto($autoId)
+                    ->setCId($c_id)
+                    ->setQuestionId($questionId)
+                    ->setAnswer($answer)
+                    ->setCorrect($correct)
+                    ->setComment($comment)
+                    ->setPonderation($weighting)
+                    ->setPosition($position)
+                    ->setHotspotCoordinates($hotspot_coordinates)
+                    ->setHotspotType($hotspot_type)
+                    ->setDestination($destination);
+
+                $em->persist($quizAnswer);
+                $em->flush();
+
+                $iid = $quizAnswer->getIid();
+
                 if ($iid) {
-                    $sql = "UPDATE $answerTable SET id = iid, id_auto = iid WHERE iid = $iid";
-                    Database::query($sql);
+                    $quizAnswer
+                        ->setId($iid)
+                        ->setIdAuto($iid);
+
+                    $em->merge($quizAnswer);
+                    $em->flush();
 
                     $questionType = $this->getQuestionType();
 
@@ -659,11 +671,10 @@ class Answer
 
                         $correctAnswerAutoId = $answer->selectAutoId($correct);
 
-                        Database::update(
-                            $answerTable,
-                            ['correct' => $correctAnswerAutoId ? $correctAnswerAutoId : 0],
-                            ['iid = ?' => $iid]
-                        );
+                        $quizAnswer->setCorrect($correctAnswerAutoId ? $correctAnswerAutoId : 0);
+
+                        $em->merge($quizAnswer);
+                        $em->flush();
                     }
                 }
             } else {
@@ -751,7 +762,6 @@ class Answer
             $course_info = $this->course;
         }
 
-		$TBL_REPONSES = Database :: get_course_table(TABLE_QUIZ_ANSWER);
         $fixed_list = array();
 
         if (self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE ||
@@ -798,7 +808,6 @@ class Answer
                     );
                 }
 
-				$answer = $this->answer[$i];
 				$correct = $this->correct[$i];
 
                 if (self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE ||
@@ -807,31 +816,30 @@ class Answer
                     $correct = $fixed_list[intval($correct)];
                 }
 
-				$comment = $this->comment[$i];
-				$weighting = $this->weighting[$i];
-				$position = $this->position[$i];
-				$hotspot_coordinates = $this->hotspot_coordinates[$i];
-				$hotspot_type = $this->hotspot_type[$i];
-				$destination = $this->destination[$i];
-
-                $params = [
-                    'c_id' => $c_id,
-                    'question_id' => $newQuestionId,
-                    'answer' => $answer,
-                    'correct' => $correct,
-                    'comment' => $comment,
-                    'ponderation' => $weighting,
-                    'position' => $position,
-                    'hotspot_coordinates' => $hotspot_coordinates,
-                    'hotspot_type' => $hotspot_type,
-                    'destination' => $destination
-                ];
-                $id = Database::insert($TBL_REPONSES, $params);
-
-                if ($id) {
-                    $sql = "UPDATE $TBL_REPONSES SET id = iid, id_auto = iid WHERE iid = $id";
-                    Database::query($sql);
-                }
+                $quizAnswer = new \Chamilo\CourseBundle\Entity\CQuizAnswer();
+                $quizAnswer
+                    ->setCId($c_id)
+                    ->setQuestionId($newQuestionId)
+                    ->setAnswer($this->answer[$i])
+                    ->setCorrect($correct)
+                    ->setComment($this->comment[$i])
+                    ->setPonderation($this->weighting[$i])
+                    ->setPosition($this->position[$i])
+                    ->setHotspotCoordinates($this->hotspot_coordinates[$i])
+                    ->setHotspotType($this->hotspot_type[$i])
+                    ->setDestination($this->destination[$i]);
+
+                $em = Database::getManager();
+
+                $em->persist($quizAnswer);
+                $em->flush();
+
+                $quizAnswer
+                    ->setId($quizAnswer->getIid())
+                    ->setIdAuto($quizAnswer->getIid());
+
+                $em->merge($quizAnswer);
+                $em->flush();
 			}
         }
 	}

+ 1 - 0
main/exercise/export/aiken/aiken_classes.php

@@ -13,6 +13,7 @@ if (!function_exists('mime_content_type')) {
 
 	/**
 	 * @param string $filename
+     * @return string
 	 */
 	function mime_content_type($filename) {
 		return DocumentManager::file_get_mime_type((string)$filename);

+ 150 - 42
main/exercise/export/exercise_import.inc.php

@@ -1,32 +1,13 @@
 <?php
+/* For licensing terms, see /license.txt */
 /**
  * @copyright (c) 2001-2006 Universite catholique de Louvain (UCL)
- *
- * @license http://www.gnu.org/copyleft/gpl.html (GPL) GENERAL PUBLIC LICENSE
- *
  * @package chamilo.exercise
- * @deprecated
- *
  * @author claro team <cvs@claroline.net>
  * @author Guillaume Lederer <guillaume@claroline.net>
+ * @author Yannick Warnier <yannick.warnier@beeznest.com>
  */
 
-/**
- * function to create a temporary directory (SAME AS IN MODULE ADMIN)
- */
-function tempdir($dir, $prefix = 'tmp', $mode = 0777)
-{
-    if (substr($dir, -1) != '/') {
-        $dir .= '/';
-    }
-
-    do {
-        $path = $dir.$prefix.mt_rand(0, 9999999);
-    } while (!mkdir($path, $mode));
-
-    return $path;
-}
-
 /**
  * Unzip the exercise in the temp folder
  * @param string The path of the temporary directory where the exercise was uploaded and unzipped
@@ -76,9 +57,9 @@ function import_exercise($file)
     global $record_item_body;
     // used to specify the question directory where files could be found in relation in any question
     global $questionTempDir;
+    global $resourcesLinks;
 
-    $archive_path = api_get_path(SYS_ARCHIVE_PATH) . 'qti2';
-    $baseWorkDir = $archive_path;
+    $baseWorkDir = api_get_path(SYS_ARCHIVE_PATH) . 'qti2';
 
     if (!is_dir($baseWorkDir)) {
         mkdir($baseWorkDir, api_get_permissions_for_new_directories(), true);
@@ -110,32 +91,50 @@ function import_exercise($file)
     }
 
     // find the different manifests for each question and parse them.
-
     $exerciseHandle = opendir($baseWorkDir);
     //$question_number = 0;
     $file_found = false;
     $operation = false;
     $result = false;
     $filePath = null;
+    $resourcesLinks = array();
 
-    // parse every subdirectory to search xml question files
+    // parse every subdirectory to search xml question files and other assets to be imported
+    // The assets-related code is a bit fragile as it has to deal with files renamed by Chamilo and it only works if
+    // the imsmanifest.xml file is read.
     while (false !== ($file = readdir($exerciseHandle))) {
         if (is_dir($baseWorkDir . '/' . $file) && $file != "." && $file != "..") {
             // Find each manifest for each question repository found
             $questionHandle = opendir($baseWorkDir . '/' . $file);
+            // Only analyse one level of subdirectory - no recursivity here
             while (false !== ($questionFile = readdir($questionHandle))) {
                 if (preg_match('/.xml$/i', $questionFile)) {
-                    $result = parse_file($baseWorkDir, $file, $questionFile);
-                    $filePath = $baseWorkDir . $file;
-                    $file_found = true;
+                    $isQti = isQtiQuestionBank($baseWorkDir . '/' . $file . '/' . $questionFile);
+                    if ($isQti) {
+                        $result = qti_parse_file($baseWorkDir, $file, $questionFile);
+                        $filePath = $baseWorkDir . $file;
+                        $file_found = true;
+                    } else {
+                        $isManifest = isQtiManifest($baseWorkDir . '/' . $file . '/' . $questionFile);
+                        if ($isManifest) {
+                            $resourcesLinks = qtiProcessManifest($baseWorkDir . '/' . $file . '/' . $questionFile);
+                        }
+                    }
                 }
             }
         } elseif (preg_match('/.xml$/i', $file)) {
+            $isQti = isQtiQuestionBank($baseWorkDir . '/' . $file);
+            if ($isQti) {
+                $result = qti_parse_file($baseWorkDir, '', $file);
+                $filePath = $baseWorkDir . '/' . $file;
+                $file_found = true;
+            } else {
+                $isManifest = isQtiManifest($baseWorkDir . '/' . $file);
+                if ($isManifest) {
+                    $resourcesLinks = qtiProcessManifest($baseWorkDir . '/' . $file);
+                }
+            }
 
-            // Else ignore file
-            $result = parse_file($baseWorkDir, '', $file);
-            $filePath = $baseWorkDir . '/' . $file;
-            $file_found = true;
         }
     }
 
@@ -234,7 +233,7 @@ function formatText($text)
  * @param string $questionFile
  * @return bool
  */
-function parse_file($exercisePath, $file, $questionFile)
+function qti_parse_file($exercisePath, $file, $questionFile)
 {
     global $non_HTML_tag_to_avoid;
     global $record_item_body;
@@ -252,7 +251,8 @@ function parse_file($exercisePath, $file, $questionFile)
     }
 
     //parse XML question file
-    $data = str_replace(array('<p>', '</p>', '<front>', '</front>'), '', $data);
+    //$data = str_replace(array('<p>', '</p>', '<front>', '</front>'), '', $data);
+    $data = stripGivenTags($data, array('p', 'front'));
     $qtiVersion = array();
     $match = preg_match('/ims_qtiasiv(\d)p(\d)/', $data, $qtiVersion);
     $qtiMainVersion = 2; //by default, assume QTI version 2
@@ -292,7 +292,14 @@ function parse_file($exercisePath, $file, $questionFile)
     if (!xml_parse($xml_parser, $data, feof($fp))) {
         // if reading of the xml file in not successful :
         // set errorFound, set error msg, break while statement
-        Display:: display_error_message(get_lang('Error reading XML file'));
+        $error = xml_get_error_code();
+        Display::addFlash(
+            Display::return_message(
+                get_lang('Error reading XML file') . sprintf('[%d:%d]', xml_get_current_line_number($xml_parser), xml_get_current_column_number($xml_parser)),
+                'error'
+            )
+        );
+
         return false;
     }
 
@@ -310,6 +317,7 @@ function parse_file($exercisePath, $file, $questionFile)
                 'error'
             )
         );
+
         return false;
     }
     return true;
@@ -517,6 +525,7 @@ function elementDataQti2($parser, $data)
     global $non_HTML_tag_to_avoid;
     global $current_inlinechoice_id;
     global $cardinality;
+    global $resourcesLinks;
 
     $current_element = end($element_pile);
     if (sizeof($element_pile) >= 2) {
@@ -564,6 +573,13 @@ function elementDataQti2($parser, $data)
             }
             break;
         case 'ITEMBODY':
+            // Replace relative links by links to the documents in the course
+            // $resourcesLinks is only defined by qtiProcessManifest()
+            if (isset($resourcesLinks) && isset($resourcesLinks['manifest']) && isset($resourcesLinks['web'])) {
+                foreach ($resourcesLinks['manifest'] as $key => $value) {
+                    $data = preg_replace('|' . $value . '|', $resourcesLinks['web'][$key], $data);
+                }
+            }
             $current_question_item_body .= $data;
             break;
         case 'INLINECHOICE':
@@ -667,14 +683,9 @@ function startElementQti1($parser, $name, $attributes)
             $exercise_info['question'][$current_question_ident] = array();
             $exercise_info['question'][$current_question_ident]['answer'] = array();
             $exercise_info['question'][$current_question_ident]['correct_answers'] = array();
-            //$exercise_info['question'][$current_question_ident]['title'] = $attributes['TITLE'];
             $exercise_info['question'][$current_question_ident]['tempdir'] = $questionTempDir;
             break;
         case 'SECTION':
-            //retrieve exercise name
-            //if (isset($attributes['TITLE']) && !empty($attributes['TITLE'])) {
-            //    $exercise_info['name'] = $attributes['TITLE'];
-            //}
             break;
         case 'RESPONSE_LID':
             // Retrieve question type
@@ -719,7 +730,6 @@ function startElementQti1($parser, $name, $attributes)
             }
             break;
         case 'IMG':
-            //$exercise_info['question'][$current_question_ident]['attached_file_url'] = $attributes['SRC'];
             break;
         case 'MATTEXT':
             if ($parent_element == 'MATERIAL') {
@@ -748,6 +758,7 @@ function endElementQti1($parser, $name, $attributes)
     global $cardinality;
     global $lastLabelFieldName;
     global $lastLabelFieldValue;
+    global $resourcesLinks;
 
     $current_element = end($element_pile);
     if (sizeof($element_pile) >= 2) {
@@ -775,7 +786,9 @@ function endElementQti1($parser, $name, $attributes)
     switch ($name) {
         case 'MATTEXT':
             if ($parent_element == 'MATERIAL') {
-                if ($grand_parent_element == 'PRESENTATION') {
+                // For some reason an item in a hierarchy <item><presentation><material><mattext> doesn't seem to
+                // catch the grandfather 'presentation', so we check for 'item' as a patch (great-grand-father)
+                if ($grand_parent_element == 'PRESENTATION' OR $grand_parent_element == 'ITEM') {
                     $exercise_info['question'][$current_question_ident]['title'] = $current_question_item_body;
                     $current_question_item_body = '';
                 } elseif ($grand_parent_element == 'RESPONSE_LABEL') {
@@ -828,6 +841,7 @@ function elementDataQti1($parser, $data)
     global $cardinality;
     global $lastLabelFieldName;
     global $lastLabelFieldValue;
+    global $resourcesLinks;
 
     $current_element = end($element_pile);
     if (sizeof($element_pile) >= 2) {
@@ -879,6 +893,13 @@ function elementDataQti1($parser, $data)
             }
             break;
         case 'MATTEXT':
+            // Replace relative links by links to the documents in the course
+            // $resourcesLinks is only defined by qtiProcessManifest()
+            if (isset($resourcesLinks) && isset($resourcesLinks['manifest']) && isset($resourcesLinks['web'])) {
+                foreach ($resourcesLinks['manifest'] as $key=>$value) {
+                    $data = preg_replace('|' . $value . '|', $resourcesLinks['web'][$key], $data);
+                }
+            }
             if (!empty($current_question_item_body)) {
                 $current_question_item_body .= $data;
             } else {
@@ -900,3 +921,90 @@ function elementDataQti1($parser, $data)
 
     }
 }
+
+/**
+ * Check if a given file is an IMS/QTI question bank file
+ * @param string $filePath The absolute filepath
+ * @return bool Whether it is an IMS/QTI question bank or not
+ */
+function isQtiQuestionBank($filePath)
+{
+    $data = file_get_contents($filePath);
+    if (!empty($data)) {
+        $match = preg_match('/ims_qtiasiv(\d)p(\d)/', $data);
+        if ($match) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+/**
+ * Check if a given file is an IMS/QTI manifest file (listing of extra files)
+ * @param string $filePath The absolute filepath
+ * @return bool Whether it is an IMS/QTI manifest file or not
+ */
+function isQtiManifest($filePath)
+{
+    $data = file_get_contents($filePath);
+    if (!empty($data)) {
+        $match = preg_match('/imsccv(\d)p(\d)/', $data);
+        if ($match) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+/**
+ * Processes an IMS/QTI manifest file: store links to new files to be able to transform them into the questions text
+ * @param string $filePath The absolute filepath
+ * @param array $links List of filepaths changes
+ * @return bool
+ */
+function qtiProcessManifest($filePath)
+{
+    $xml = simplexml_load_file($filePath);
+    $course = api_get_course_info();
+    $sessionId = api_get_session_id();
+    $courseDir = $course['path'];
+    $sysPath = api_get_path(SYS_COURSE_PATH);
+    $exercisesSysPath = $sysPath . $courseDir . '/document/';
+    $webPath = api_get_path(WEB_CODE_PATH);
+    $exercisesWebPath = $webPath . 'document/document.php?' . api_get_cidreq() . '&action=download&id=';
+    $links = array(
+        'manifest' => array(),
+        'system' => array(),
+        'web' => array(),
+    );
+    $tableDocuments = Database::get_course_table(TABLE_DOCUMENT);
+    $countResources = count($xml->resources->resource->file);
+    for ($i=0; $i < $countResources; $i++) {
+        $file = $xml->resources->resource->file[$i];
+        $href = '';
+        foreach ($file->attributes() as $key => $value) {
+            if ($key == 'href') {
+                if (substr($value, -3, 3) != 'xml') {
+                    $href = $value;
+                }
+            }
+        }
+        if (!empty($href)) {
+            $links['manifest'][] = (string) $href;
+            $links['system'][] = $exercisesSysPath . strtolower($href);
+            $specialHref = Database::escape_string(preg_replace('/_/', '-', strtolower($href)));
+            $specialHref = preg_replace('/(-){2,8}/', '-', $specialHref);
+
+            $sql = "SELECT iid FROM " . $tableDocuments . " WHERE c_id = " . $course['real_id'] . " AND session_id = $sessionId AND path = '/" . $specialHref . "'";
+            $result = Database::query($sql);
+            $documentId = 0;
+            while ($row = Database::fetch_assoc($result)) {
+                $documentId = $row['iid'];
+            }
+            $links['web'][] = $exercisesWebPath . $documentId;
+        }
+    }
+    return $links;
+}

+ 6 - 3
main/exercise/export/qti2/qti2_export.php

@@ -34,7 +34,7 @@ class ImsAssessmentItem
      *
      * @param Ims2Question $question Ims2Question object we want to export.
      */
-     function ImsAssessmentItem($question)
+     function __construct($question)
      {
         $this->question = $question;
         $this->answer = $this->question->setAnswer();
@@ -155,9 +155,10 @@ class ImsSection
     /**
      * Constructor.
      * @param Exercise $exe The Exercise instance to export
+     * @return ImsSection
      * @author Amand Tihon <amand@alrj.org>
      */
-    function ImsSection($exe)
+    function __construct($exe)
     {
         $this->exercise = $exe;
     }
@@ -302,9 +303,10 @@ class ImsItem
      * Constructor.
      *
      * @param $question The Question object we want to export.
+     * @return ImsItem
      * @author Anamd Tihon
      */
-     function ImsItem($question)
+     function __construct($question)
      {
         $this->question = $question;
         $this->answer = $question->answer;
@@ -435,6 +437,7 @@ function export_exercise_to_qti($exerciseId, $standalone = true)
  *
  * @param int $questionId
  * @param bool $standalone (ie including XML tag, DTD declaration, etc)
+ * @return string
  */
 function export_question_qti($questionId, $standalone = true)
 {

+ 2 - 1
main/exercise/qti2.php

@@ -26,7 +26,7 @@ $interbreadcrumb[]= array (
 $is_allowedToEdit = api_is_allowed_to_edit(null, true);
 
 /**
- * This function displays the form for import of the zip file with qti2
+ * This function displays the form to import the zip file with qti2
  */
 function ch_qti2_display_form()
 {
@@ -52,6 +52,7 @@ function ch_qti2_display_form()
 /**
  * This function will import the zip file with the respective qti2
  * @param array $array_file ($_FILES)
+ * @return string|array
  */
 function ch_qti2_import_file($array_file)
 {

+ 42 - 34
main/exercise/question.class.php

@@ -795,6 +795,7 @@ abstract class Question
     {
         $TBL_EXERCISE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
         $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
+        $em = Database::getManager();
 
         $id = $this->id;
         $question = $this->question;
@@ -892,47 +893,54 @@ abstract class Question
 
                 // If hotspot, create first answer
                 if ($type == HOT_SPOT || $type == HOT_SPOT_ORDER) {
-                    $TBL_ANSWERS = Database::get_course_table(
-                        TABLE_QUIZ_ANSWER
-                    );
-                    $params = [
-                        'c_id' => $c_id,
-                        'question_id' => $this->id,
-                        'answer' => '',
-                        'correct' => '',
-                        'comment' => '',
-                        'ponderation' => 10,
-                        'position' => 1,
-                        'hotspot_coordinates' => '0;0|0|0',
-                        'hotspot_type' => 'square',
-                    ];
-                    $id = Database::insert($TBL_ANSWERS, $params);
+                    $quizAnswer = new \Chamilo\CourseBundle\Entity\CQuizAnswer();
+                    $quizAnswer
+                        ->setCId($c_id)
+                        ->setQuestionId($this->id)
+                        ->setAnswer('')
+                        ->setPonderation(10)
+                        ->setPosition(1)
+                        ->setHotspotCoordinates('0;0|0|0')
+                        ->setHotspotType('square');
+
+                    $em->persist($quizAnswer);
+                    $em->flush();
+
+                    $id = $quizAnswer->getIid();
+
                     if ($id) {
-                        $sql = "UPDATE $TBL_ANSWERS SET id = iid, id_auto = iid WHERE iid = $id";
-                        Database::query($sql);
+                        $quizAnswer
+                            ->setId($id)
+                            ->setIdAuto($id);
+
+                        $em->merge($quizAnswer);
+                        $em->flush();
                     }
                 }
 
                 if ($type == HOT_SPOT_DELINEATION) {
-                    $TBL_ANSWERS = Database::get_course_table(
-                        TABLE_QUIZ_ANSWER
-                    );
-                    $params = [
-                        'c_id' => $c_id,
-                        'question_id' => $this->id,
-                        'answer' => '',
-                        'correct' => '',
-                        'comment' => '',
-                        'ponderation' => 10,
-                        'position' => 1,
-                        'hotspot_coordinates' => '0;0|0|0',
-                        'hotspot_type' => 'delineation',
-                    ];
-                    $id = Database::insert($TBL_ANSWERS, $params);
+                    $quizAnswer = new \Chamilo\CourseBundle\Entity\CQuizAnswer();
+                    $quizAnswer
+                        ->setCId($c_id)
+                        ->setQuestionId($this->id)
+                        ->setAnswer('')
+                        ->setPonderation(10)
+                        ->setPosition(1)
+                        ->setHotspotCoordinates('0;0|0|0')
+                        ->setHotspotType('delineation');
+
+                    $em->persist($quizAnswer);
+                    $em->flush();
+
+                    $id = $quizAnswer->getIid();
 
                     if ($id) {
-                        $sql = "UPDATE $TBL_ANSWERS SET id = iid, id_auto = iid WHERE iid = $id";
-                        Database::query($sql);
+                        $quizAnswer
+                            ->setId($id)
+                            ->setIdAuto($id);
+
+                        $em->merge($quizAnswer);
+                        $em->flush();
                     }
                 }
 

+ 22 - 15
main/exercise/unique_answer.class.php

@@ -429,6 +429,7 @@ class UniqueAnswer extends Question
         $score = 0.0,
         $correct = 0
     ) {
+        $em = Database::getManager();
         $tbl_quiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER);
         $tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
         $course_id = api_get_course_int_id();
@@ -448,23 +449,29 @@ class UniqueAnswer extends Question
         $position = $row_max->max_position + 1;
 
         // Insert a new answer
-       $params = [
-            'c_id' => $course_id,
-            'id' => $id,
-            'question_id' => $question_id,
-            'answer' => $title,
-            'correct' => $correct,
-            'comment' => $comment,
-            'ponderation' => $score,
-            'position' => $position,
-            'destination' => '0@@0@@0@@0',
-        ];
-
-        $id = Database::insert($tbl_quiz_answer, $params);
+        $quizAnswer = new \Chamilo\CourseBundle\Entity\CQuizAnswer();
+        $quizAnswer
+            ->setCId($course_id)
+            ->setId($id)
+            ->setQuestionId($question_id)
+            ->setAnswer($title)
+            ->setCorrect($correct)
+            ->setComment($comment)
+            ->setPonderation($score)
+            ->setPosition($position)
+            ->setDestination('0@@0@@0@@0');
+
+        $em->persist($quizAnswer);
+        $em->flush();
+
+        $id = $quizAnswer->getIid();
 
         if ($id) {
-            $sql = "UPDATE $tbl_quiz_answer SET id = iid WHERE iid = $id";
-            Database::query($sql);
+            $quizAnswer
+                ->setId($id);
+
+            $em->merge($quizAnswer);
+            $em->flush();
         }
 
         if ($correct) {

+ 2 - 2
main/inc/ajax/work.ajax.php

@@ -41,7 +41,7 @@ switch ($action) {
                     'title' => $file['name'],
                     'description' => ''
                 ];
-                $result = processWorkForm($workInfo, $values, $courseInfo, $sessionId, 0, $userId, $file);
+                $result = processWorkForm($workInfo, $values, $courseInfo, $sessionId, 0, $userId, $file, true);
 
                 $json = array();
                 if (!empty($result) && is_array($result) && empty($result['error'])) {
@@ -60,7 +60,7 @@ switch ($action) {
                     );
                 } else {
                     $json['url'] = '';
-                    $json['error'] = get_lang('Error');
+                    $json['error'] = isset($result['error']) ? $result['error'] : get_lang('Error');
                 }
                 $resultList[] = $json;
             }

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

@@ -8029,3 +8029,19 @@ function api_protect_limit_for_session_admin()
 function api_is_student_view_active() {
     return (isset($_SESSION['studentview']) && $_SESSION['studentview'] == "studentview");
 }
+
+/**
+ * Like strip_tags(), but leaves an additional space and removes only the given tags
+ * @param string $string
+ * @param array $tags Tags to be removed
+ * @return  string The original string without the given tags
+ */
+function stripGivenTags($string, $tags) {
+    foreach ($tags as $tag) {
+        $string2 = preg_replace('#</' . $tag . '[^>]*>#i', ' ', $string);
+        if ($string2 != $string) {
+            $string = preg_replace('/<' . $tag . '[^>]*>/i', ' ', $string2);
+        }
+    }
+    return $string;
+}

+ 5 - 58
main/inc/lib/banner.lib.php

@@ -6,6 +6,8 @@
  * @package chamilo.include
  */
 
+use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
+
 /**
  * Determines the possible tabs (=sections) that are available.
  * This function is used when creating the tabs in the third header line and
@@ -173,68 +175,13 @@ function getCustomTabs()
  */
 function return_logo($theme)
 {
-    $_course = api_get_course_info();
-    $html = '';
-    $logoBase = api_get_path(SYS_CSS_PATH).'themes/'.$theme.'/images/header-logo.';
-    $customLogoBase = api_get_path(SYS_PUBLIC_PATH).'css/themes/'.$theme.'/images/header-logo-custom.';
-    $customLogo = '';
     $siteName = api_get_setting('siteName');
-    $attributes = array(
+
+    return ChamiloApi::getPlatformLogo([
         'title' => $siteName,
         'class' => 'img-responsive',
         'id' => 'header-logo'
-    );
-    $testServer = api_get_setting('server_type');
-    if ($testServer == 'test' && is_file($logoBase . 'svg')) {
-        $logo = $logoBase . 'svg';
-        $attributes['width'] = '245';
-        $attributes['height'] = '68';
-        $imageUrl = api_get_path(WEB_CSS_PATH).'themes/'.$theme.'/images/header-logo.svg';
-    } else {
-        $logo = $logoBase . 'png';
-        $customLogo = $customLogoBase . 'png';
-        $imageUrl = api_get_path(WEB_CSS_PATH).'themes/'.$theme.'/images/header-logo.png';
-        $customImageUrl = api_get_path(WEB_CSS_PATH).'themes/'.$theme.'/images/header-logo-custom.png';
-    }
-    if (file_exists($customLogo)) {
-        $siteName = api_get_setting('Institution').' - '.$siteName;
-        $customLogo = Display::img(
-            $customImageUrl,
-            $siteName,
-            $attributes
-        );
-        $html .= Display::url($customLogo, api_get_path(WEB_PATH).'index.php');
-    } elseif (file_exists($logo)) {
-        $siteName = api_get_setting('Institution').' - '.$siteName;
-        $logo = Display::img(
-            $imageUrl,
-            $siteName,
-            $attributes
-        );
-        $html .= Display::url($logo, api_get_path(WEB_PATH).'index.php');
-    } else {
-        $html .= '<a href="'.api_get_path(WEB_PATH).'index.php" target="_top">'.$siteName.'</a>';
-        $iurl = api_get_setting('InstitutionUrl');
-        $iname = api_get_setting('Institution');
-
-        if (!empty($iname)) {
-            $html .= '-&nbsp;<a href="'.$iurl.'" target="_top">'.$iname.'</a>';
-        }
-
-        // External link section a.k.a Department - Department URL
-        if (isset($_course['extLink']) && $_course['extLink']['name'] != '') {
-            $html .= '<span class="extLinkSeparator"> - </span>';
-            if ($_course['extLink']['url'] != '') {
-                $html .= '<a class="extLink" href="'.$_course['extLink']['url'].'" target="_top">';
-                $html .= $_course['extLink']['name'];
-                $html .= '</a>';
-            } else {
-                $html .= $_course['extLink']['name'];
-            }
-        }
-    }
-
-    return $html;
+    ]);
 }
 
 /**

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

@@ -1,6 +1,8 @@
 <?php
 /* For licensing terms, see /license.txt */
 
+use ChamiloSession as Session;
+
 /**
  * Class FormValidator
  * create/manipulate/validate user input.
@@ -1442,6 +1444,9 @@ EOT;
                     node
                         .prepend('<br>')
                         .prepend(file.preview);
+                    node
+                        .append('<br>')
+                        .append($('<span class=\"text-success\"/>').text('" . get_lang('UplUploadSucceeded') . "'));
                 }
                 if (file.error) {
                     node
@@ -1460,7 +1465,6 @@ EOT;
                     progress + '%'
                 );
             }).on('fileuploaddone', function (e, data) {
-
                 $.each(data.result.files, function (index, file) {
                     if (file.url) {
                         var link = $('<a>')
@@ -1477,7 +1481,8 @@ EOT;
                 });
             }).on('fileuploadfail', function (e, data) {
                 $.each(data.files, function (index) {
-                    var error = $('<span class=\"text-danger\"/>').text('".get_lang('UploadError')."');
+                    var failedMessage = '" . get_lang('UplUploadFailed') . "';
+                    var error = $('<span class=\"text-danger\"/>').text(failedMessage);
                     $(data.context.children()[index])
                         .append('<br>')
                         .append(error);

+ 6 - 15
main/inc/lib/pdf.lib.php

@@ -1,6 +1,8 @@
 <?php
 /* See license terms in /license.txt */
 
+use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
+
 /**
  * Class PDF
  * @package chamilo.library
@@ -87,24 +89,13 @@ class PDF
         } else {
             $tpl = $this->template;
         }
-        // Assignments
-        // Assignments
-        $tpl->assign('pdf_content', $content);
 
-        $organization = api_get_setting('Institution');
-        $img = api_get_path(SYS_CSS_PATH).'themes/'.api_get_visual_theme().'/images/header-logo.png';
+        $theme = api_get_visual_theme();
 
-        // Search for classic logo
-        if (file_exists($img)) {
-            $img = api_get_path(WEB_CSS_PATH).'themes/'.api_get_visual_theme().'/images/header-logo.png';
-            $organization = "<img src='$img'>";
-        } else {
-            // Just use the platform title.
-            if (!empty($organization)) {
-                $organization = '<h2 align="left">'.$organization.'</h2>';
-            }
-        }
+        // Assignments
+        $tpl->assign('pdf_content', $content);
 
+        $organization = ChamiloApi::getPlatformLogo();
         // Use custom logo image.
         $pdfLogo = api_get_setting('pdf_logo_header');
         if ($pdfLogo === 'true') {

+ 1 - 0
main/inc/lib/system/session.class.php

@@ -39,6 +39,7 @@ class Session implements \ArrayAccess
      * Returns true if session has variable set up, false otherwise.
      *
      * @param string $variable
+     * @return mixed value
      */
     static function has($variable)
     {

+ 2 - 0
main/inc/lib/template.lib.php

@@ -124,6 +124,7 @@ class Template
         $this->twig->addFilter('get_path', new Twig_Filter_Function('api_get_path'));
         $this->twig->addFilter('get_setting', new Twig_Filter_Function('api_get_setting'));
         $this->twig->addFilter('var_dump', new Twig_Filter_Function('var_dump'));
+        $this->twig->addFilter('return_logo', new Twig_Filter_Function('return_logo'));
         $this->twig->addFilter('return_message', new Twig_Filter_Function('Display::return_message_and_translate'));
         $this->twig->addFilter('display_page_header', new Twig_Filter_Function('Display::page_header_and_translate'));
         $this->twig->addFilter(
@@ -221,6 +222,7 @@ class Template
     {
         return Display::return_icon($image, $name, array(), $size);
     }
+
     /**
      * @param string $timestamp
      * @param string $format

+ 1 - 1
main/inc/lib/userportal.lib.php

@@ -1149,7 +1149,7 @@ class IndexManager
                 );
             }
 
-            if ($courses) {
+            if ($courses['in_category'] || $courses['not_category']) {
                 $template->assign('courses', $courses['not_category']);
                 $template->assign('categories', $courses['in_category']);
 

+ 1 - 0
main/install/index.php

@@ -131,6 +131,7 @@ $update_from_version_8 = array(
     '1.10.2',
     '1.10.4',
     '1.10.6',
+    '1.10.8',
 );
 
 $my_old_version = '';

+ 2 - 0
main/install/install.lib.php

@@ -2987,6 +2987,8 @@ function migrateSwitch($fromVersion, $manager, $processFiles = true)
         case '1.10.4':
             // no break
         case '1.10.6':
+            // no break
+        case '1.10.8':
             // Migrate using the migration files located in:
             // src/Chamilo/CoreBundle/Migrations/Schema/V111
             $result = migrate(

+ 1 - 1
main/install/version.php

@@ -14,7 +14,7 @@
  * Variables used from the main/install/index.php
  */
 $new_version = '1.11.0';
-$new_version_status = 'alpha';
+$new_version_status = 'beta';
 $new_version_last_id = 0;
 $new_version_stable = false;
 $new_version_major = true;

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

@@ -7847,4 +7847,8 @@ $Mask = "Mask";
 $Icon = "Icon";
 $DesignWithBadgeStudio = "Design with Badge Studio";
 $UseThisBadge = "Use this badge";
+$Palettes = "Palettes";
+$DesignWithBadgeStudioComment = "Usa Badge Studio para crear tu propia insignia dentro de tu plataforma";
+$YouAlreadySentThisFile = "You have already sent this file or another file with the same name. Please make sure you only upload each file once.";
+$DocumentsDefaultVisibility = "Default visibility of new documents";
 ?>

+ 4 - 0
main/lang/french/trad4all.inc.php

@@ -7794,4 +7794,8 @@ $Mask = "Masque";
 $Icon = "Icône";
 $DesignWithBadgeStudio = "Dessiner avec Badge Studio";
 $UseThisBadge = "Utiliser ce badge";
+$Palettes = "Palettes";
+$DesignWithBadgeStudioComment = "Utilisez le designer de badges intégré pour créer votre propre badge.";
+$YouAlreadySentThisFile = "Vous avez déjà envoyé ce fichier ou un autre fichier du même nom. Veuillez vous assurer de n'envoyer qu'une seule fois chaque fichier.";
+$DocumentsDefaultVisibility = "Visibilité par défaut des nouveaux documents";
 ?>

+ 4 - 0
main/lang/spanish/trad4all.inc.php

@@ -7866,4 +7866,8 @@ $Mask = "Máscara";
 $Icon = "Icono";
 $DesignWithBadgeStudio = "Diseñar con Badge Studio";
 $UseThisBadge = "Usar esta insignia";
+$Palettes = "Paletas";
+$DesignWithBadgeStudioComment = "Use the Badge Studio to create your own badge from within your platform";
+$YouAlreadySentThisFile = "Ya envió este archivo u otro archivo con el mismo nombre. Por favor asegúrese de enviar cada archivo una sola vez.";
+$DocumentsDefaultVisibility = "Visibilidad por defecto de los documentos";
 ?>

+ 2 - 1
main/session/about.php

@@ -149,7 +149,8 @@ $template->assign(
 );
 
 $template->assign('courses', $courses);
-$template->assign('essence', \Essence\Essence::instance());
+$essence = new Essence\Essence();
+$template->assign('essence', $essence);
 $template->assign(
     'session_extra_fields',
     $sessionValues->getAllValuesForAnItem($session->getId(), true)

+ 3 - 3
main/template/default/learnpath/view.tpl

@@ -135,9 +135,9 @@
                             <div role="tabpanel" class="tab-pane active" id="lp-view-content">
                                 <div id="wrapper-iframe" style="width:100%; height:100%">
                                 {% if lp_mode == 'fullscreen' %}
-                                    <iframe id="content_id_blank" name="content_name_blank" style="width:100%; height:100%" src="blank.php" border="0" frameborder="0" allowfullscreen="true" webkitallowfullscreen="true" mozallowfullscreen="true"></iframe>
+                                    <iframe id="content_id_blank" name="content_name_blank" src="blank.php" style="width:100%; height:100%" border="0" frameborder="0" allowfullscreen="true" webkitallowfullscreen="true" mozallowfullscreen="true"></iframe>
                                 {% else %}
-                                    <iframe id="content_id" name="content_name" style="width:100%; height:100%" src="{{ iframe_src }}" border="0" frameborder="0" allowfullscreen="true" webkitallowfullscreen="true" mozallowfullscreen="true"></iframe>
+                                    <iframe id="content_id" name="content_name" src="{{ iframe_src }}" style="width:100%; height:100%" border="0" frameborder="0" allowfullscreen="true" webkitallowfullscreen="true" mozallowfullscreen="true"></iframe>
                                 {% endif %}
                                 </div>
                             </div>
@@ -171,7 +171,7 @@
                 '-webkit-overflow-scrolling': 'touch'
               });
             }
-            
+
             {% if lp_mode == 'embedframe' %}
                 //$('#learning_path_main').addClass('lp-view-collapsed');
                 $('#lp-view-expand-button, #lp-view-expand-toggle').on('click', function (e) {

+ 1 - 3
main/template/default/mail/header.tpl

@@ -1,9 +1,7 @@
 <table border="0" cellpadding="10" cellspacing="0" width="100%">
     <tr>
         <td width="245">
-            <a href="{{ _p.web }}">
-                <img src="{{ _p.web_css_theme ~ 'images/header-logo.png' }}" alt="{{ _s.site_name }}">
-            </a>
+            {{ css_styles | return_logo }}
         </td>
         <td width="100%">
             &nbsp;

+ 37 - 9
main/work/work.lib.php

@@ -1370,7 +1370,7 @@ function getWorkListStudent(
         $lastWork = getLastWorkStudentFromParentByUser($userId, $work['id'], $courseInfo);
 
         if (!empty($lastWork)) {
-            $work['last_upload'] = Display::label($lastWork['qualification'], 'warning').' - ';
+            $work['last_upload'] = (!empty($lastWork['qualification'])) ? Display::label($lastWork['qualification'], 'warning').' - ' : '';
             $work['last_upload'] .= api_get_local_time($lastWork['sent_date']);
         }
 
@@ -3509,6 +3509,23 @@ function sendAlertToUsers($workId, $courseInfo, $session_id)
     }
 }
 
+/**
+ * Check if the current uploaded work filename already exists in the current assement
+ *
+ * @param $filename
+ * @param $workId
+ * @return mixed
+ */
+function checkExistingWorkFileName($filename, $workId)
+{
+    $work_table = Database :: get_course_table(TABLE_STUDENT_PUBLICATION);
+    $filename = Database::escape_string($filename);
+    $sql = "SELECT title FROM $work_table
+                        WHERE parent_id = $workId AND title = '$filename'";
+    $result = Database::query($sql);
+    return Database::fetch_assoc($result);
+}
+
 /**
  * @param array $workInfo
  * @param array $values
@@ -3517,10 +3534,11 @@ function sendAlertToUsers($workId, $courseInfo, $session_id)
  * @param int $groupId
  * @param int $userId
  * @param array $file
+ * @param bool  $checkDuplicated
  *
  * @return null|string
  */
-function processWorkForm($workInfo, $values, $courseInfo, $sessionId, $groupId, $userId, $file = [])
+function processWorkForm($workInfo, $values, $courseInfo, $sessionId, $groupId, $userId, $file = [], $checkDuplicated = false)
 {
     $work_table = Database :: get_course_table(TABLE_STUDENT_PUBLICATION);
 
@@ -3537,12 +3555,20 @@ function processWorkForm($workInfo, $values, $courseInfo, $sessionId, $groupId,
     $filename = null;
     $url = null;
     $filesize = null;
+    $workData = [];
 
     if ($values['contains_file']) {
-        $result = uploadWork($workInfo, $courseInfo, false, [], $file);
-        if (!$result) {
-            $saveWork = false;
+        if ($checkDuplicated) {
+            if (checkExistingWorkFileName($file['name'], $workInfo['id'])) {
+                $saveWork = false;
+                $workData['error'] = get_lang('YouAlreadySentThisFile');
+            } else {
+                $result = uploadWork($workInfo, $courseInfo, false, [], $file);
+            }
+        } else {
+            $result = uploadWork($workInfo, $courseInfo, false, [], $file);
         }
+
         if (isset($result['error'])) {
             $message = $result['error'];
             Display::addFlash($message);
@@ -3559,12 +3585,14 @@ function processWorkForm($workInfo, $values, $courseInfo, $sessionId, $groupId,
             $title = isset($result['title']) && !empty($result['title']) ? $result['title'] : get_lang('Untitled');
         }
         $filesize = isset($result['filesize']) ? $result['filesize'] : null;
-        $url = $result['url'];
+        $url = isset($result['url']) ? $result['url'] : null;
+    }
 
-        if (empty($title)) {
-            $title = get_lang('Untitled');
-        }
+    if (empty($title)) {
+        $title = get_lang('Untitled');
+    }
 
+    if ($saveWork) {
         $active = '1';
         $params = [
             'c_id' => $courseId,

+ 14 - 1
plugin/bbb/README.md

@@ -2,4 +2,17 @@ BigBlueButton Chamilo plugin
 ============================
 This plugin allows you to have videoconference rooms in each course.
 It requires you to have a BigBlueButton videoconference server installed on another server (ideally).
-Check http://www.bigbluebutton.org/ for more about BigBlueButton.
+Check www.bigbluebutton.org for more about BigBlueButton.
+
+## Migrating from Chamilo LMS 1.9.x to 1.10.x
+For Chamilo 1.10.x, the Videoconference plugin has two new settings options: *Enable global conference* and *Enable conference in course groups*.
+
+##### Database changes
+You need execute this SQL query en your database after making the migration process from 1.9.x.
+
+```sql
+ALTER TABLE plugin_bbb_meeting ADD voice_bridge int NOT NULL DEFAULT 1;
+ALTER TABLE plugin_bbb_meeting ADD group_id int unsigned NOT NULL DEFAULT 0;
+ALTER TABLE plugin_bbb_meeting ADD user_id int unsigned NOT NULL DEFAULT 0;
+ALTER TABLE plugin_bbb_meeting ADD access_url int NOT NULL DEFAULT 1;
+```

+ 66 - 0
src/Chamilo/CoreBundle/Component/Utils/ChamiloApi.php

@@ -63,4 +63,70 @@ class ChamiloApi
         );
     }
 
+    /**
+     * Get the platform logo path
+     * @return null|string
+     */
+    public static function getWebPlatformLogoPath()
+    {
+        $theme = api_get_visual_theme();
+        $accessUrlId = api_get_current_access_url_id();
+        $customLogoPath = "themes/$theme/images/header-logo-custom$accessUrlId.png";
+
+        if (file_exists(api_get_path(SYS_PUBLIC_PATH) . "css/$customLogoPath")) {
+            return api_get_path(WEB_CSS_PATH) . $customLogoPath;
+        }
+
+        $originalLogoPath = "themes/$theme/images/header-logo.png";
+
+        if (file_exists(api_get_path(SYS_CSS_PATH) . $originalLogoPath)) {
+            return api_get_path(WEB_CSS_PATH) . $originalLogoPath;
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the platform logo.
+     * Return a <img> if the logo image exists. Otherwise return a <h2> with the institution name.
+     * @param array $imageAttributes Optional.
+     * @return string
+     */
+    public static function getPlatformLogo($imageAttributes = [])
+    {
+        $logoPath = self::getWebPlatformLogoPath();
+        $insitution = api_get_setting('Institution');
+        $insitutionUrl = api_get_setting('InstitutionUrl');
+        $siteName = api_get_setting('siteName');
+
+        if ($logoPath === null) {
+            $headerLogo = \Display::url($siteName, api_get_path(WEB_PATH) . 'index.php');
+
+            if (!empty($insitutionUrl) && !empty($insitution)) {
+                $headerLogo .= ' - ' . \Display::url($insitution, $insitutionUrl);
+            }
+
+            $courseInfo = api_get_course_info();
+
+            if (isset($courseInfo['extLink']) && !empty($courseInfo['extLink']['name'])) {
+                $headerLogo .= '<span class="extLinkSeparator"> - </span>';
+
+                if (!empty($courseInfo['extLink']['url'])) {
+                    $headerLogo .= \Display::url(
+                        $courseInfo['extLink']['name'],
+                        $courseInfo['extLink']['url'],
+                        ['class' => 'extLink']
+                    );
+                } else if (!empty($courseInfo['extLink']['url'])) {
+                    $headerLogo .= $courseInfo['extLink']['url'];
+                }
+            }
+
+            return \Display::tag('h2', $headerLogo, ['class' => 'text-left']);
+        }
+
+        $image = \Display::img($logoPath, $insitution, $imageAttributes);
+
+        return \Display::url($image, api_get_path(WEB_PATH) . 'index.php');
+    }
 }

+ 25 - 16
src/Chamilo/CourseBundle/Component/CourseCopy/CourseRestorer.php

@@ -3,6 +3,7 @@
 
 namespace Chamilo\CourseBundle\Component\CourseCopy;
 
+use Chamilo\CourseBundle\Entity\CQuizAnswer;
 use DocumentManager;
 use Database;
 use CourseManager;
@@ -1791,6 +1792,7 @@ class CourseRestorer
 	 */
     public function restore_quiz_question($id)
     {
+        $em = Database::getManager();
 		$resources = $this->course->resources;
         $question = isset($resources[RESOURCE_QUIZQUESTION][$id]) ? $resources[RESOURCE_QUIZQUESTION][$id] : null;
 
@@ -1866,24 +1868,31 @@ class CourseRestorer
 
                 foreach ($temp as $index => $answer) {
                     //id = '".$index."',
-					$params = [
-                        'c_id' => $this->destination_course_id,
-                        'question_id' => $new_id,
-                        'answer' => self::DBUTF8($answer['answer']),
-                        'correct' => $answer['correct'],
-                        'comment' => self::DBUTF8($answer['comment']),
-                        'ponderation' => $answer['ponderation'],
-                        'position' => $answer['position'],
-                        'hotspot_coordinates' => $answer['hotspot_coordinates'],
-                        'hotspot_type' => $answer['hotspot_type'],
-                        'id_auto' => 0,
-                        'destination' => ''
-                    ];
-                    $answerId = Database::insert($table_ans, $params);
+                    $quizAnswer = new CQuizAnswer();
+                    $quizAnswer
+                        ->setCId($this->destination_course_id)
+                        ->setQuestionId($new_id)
+                        ->setAnswer(self::DBUTF8($answer['answer']))
+                        ->setCorrect($answer['correct'])
+                        ->setComment(self::DBUTF8($answer['comment']))
+                        ->setPonderation($answer['ponderation'])
+                        ->setPosition($answer['position'])
+                        ->setHotspotCoordinates($answer['hotspot_coordinates'])
+                        ->setHotspotType($answer['hotspot_type'])
+                        ->setIdAuto(0);
+
+                    $em->persist($quizAnswer);
+                    $em->flush();
+
+                    $answerId = $quizAnswer->getIid();
 
                     if ($answerId) {
-                        $sql = "UPDATE $table_ans SET id = iid, id_auto = iid WHERE iid = $answerId";
-                        Database::query($sql);
+                        $quizAnswer
+                            ->setId($answerId)
+                            ->setIdAuto($answerId);
+
+                        $em->merge($quizAnswer);
+                        $em->flush();
                     }
 				}
 			} else {

+ 24 - 4
src/Chamilo/CourseBundle/Entity/CQuizAnswer.php

@@ -80,7 +80,7 @@ class CQuizAnswer
     /**
      * @var float
      *
-     * @ORM\Column(name="ponderation", type="float", precision=6, scale=2, nullable=false)
+     * @ORM\Column(name="ponderation", type="float", precision=6, scale=2, nullable=false, options={"default": 0})
      */
     private $ponderation;
 
@@ -108,7 +108,7 @@ class CQuizAnswer
     /**
      * @var string
      *
-     * @ORM\Column(name="destination", type="text", nullable=false)
+     * @ORM\Column(name="destination", type="text", nullable=true)
      */
     private $destination;
 
@@ -119,6 +119,17 @@ class CQuizAnswer
      */
     private $answerCode;
 
+    public function __construct()
+    {
+        $this->id = null;
+        $this->correct = null;
+        $this->comment = null;
+        $this->ponderation = 0;
+        $this->hotspotCoordinates = null;
+        $this->hotspotType = null;
+        $this->destination = null;
+        $this->answerCode = null;
+    }
 
     /**
      * Set id
@@ -243,7 +254,7 @@ class CQuizAnswer
      */
     public function setPonderation($ponderation)
     {
-        $this->ponderation = $ponderation;
+        $this->ponderation = empty($ponderation) ? 0 : $ponderation;
 
         return $this;
     }
@@ -335,7 +346,7 @@ class CQuizAnswer
      */
     public function setDestination($destination)
     {
-        $this->destination = $destination;
+        $this->destination = empty($destination) ? null : $destination;
 
         return $this;
     }
@@ -418,4 +429,13 @@ class CQuizAnswer
     {
         return $this->cId;
     }
+
+    /**
+     * Get iid
+     * @return int
+     */
+    public function getIid()
+    {
+        return $this->iid;
+    }
 }

+ 10 - 2
src/Chamilo/CourseBundle/Entity/CQuizQuestion.php

@@ -15,7 +15,7 @@ use Doctrine\ORM\Mapping as ORM;
  *      @ORM\Index(name="position", columns={"position"})
  *  }
  * )
- * @ORM\Entity
+ * @ORM\Entity(repositoryClass="CQuizQuestionRepository")
  */
 class CQuizQuestion
 {
@@ -59,7 +59,7 @@ class CQuizQuestion
     /**
      * @var float
      *
-     * @ORM\Column(name="ponderation", type="float", precision=6, scale=2, nullable=false)
+     * @ORM\Column(name="ponderation", type="float", precision=6, scale=2, nullable=false, options={"default": 0})
      */
     private $ponderation;
 
@@ -105,6 +105,14 @@ class CQuizQuestion
      */
     private $questionCode;
 
+    /**
+     * CQuizQuestion constructor.
+     */
+    public function __construct()
+    {
+        $this->ponderation = 0;
+    }
+
     /**
      * Set question
      *

+ 1 - 11
tests/main/exercice/export/exercise_import.inc.test.php

@@ -67,7 +67,7 @@ class TestExerciseImport extends UnitTestCase {
 		$file = '';
 		$exercisePath = '';
 		$questionFile = '';
-		$res = parse_file($exercisePath, $file, $questionFile);
+		$res = qti_parse_file($exercisePath, $file, $questionFile);
 		$this->assertTrue(is_array($res));
 		if(!is_null){
 			$this->assertTrue($res);
@@ -86,15 +86,5 @@ class TestExerciseImport extends UnitTestCase {
 		}
 		//var_dump($res);
 	}
-	
-	function testtempdir() {
-		$dir = '/tmp';
-		$res = tempdir($dir, $prefix='tmp', $mode=0777);
-		$this->assertFalse(is_array($res));
-		if(!is_null){
-			$this->assertTrue(is_string($res));
-		}
-		//var_dump($res);
-	}
 }
 ?>