Parcourir la source

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

Alex Aragon il y a 8 ans
Parent
commit
0e8b196dc4
75 fichiers modifiés avec 806 ajouts et 797 suppressions
  1. 3 0
      .htaccess
  2. 30 3
      app/Migrations/Schema/V111/Version111.php
  3. 4 4
      app/Migrations/Schema/V111/Version20160330103045.php
  4. 3 1
      app/Migrations/Schema/V111/Version20160930144400.php
  5. 1 0
      app/Resources/public/css/base.css
  6. 8 0
      app/Resources/public/css/themes/rainbow/default.css
  7. 72 4
      documentation/changelog.html
  8. 51 59
      documentation/installation_guide.html
  9. 4 3
      documentation/optimization.html
  10. 6 7
      main/admin/grade_models.php
  11. 0 12
      main/admin/user_list.php
  12. 2 0
      main/badge/assign.php
  13. 10 3
      main/exercise/exercise.class.php
  14. 0 1
      main/exercise/exercise_result.php
  15. 2 1
      main/exercise/overview.php
  16. 15 9
      main/forum/forumfunction.inc.php
  17. 8 21
      main/forum/viewforumcategory.php
  18. 13 3
      main/gradebook/index.php
  19. 0 1
      main/inc/ajax/course_chat.ajax.php
  20. 0 2
      main/inc/ajax/online.ajax.php
  21. 3 1
      main/inc/ajax/social.ajax.php
  22. 0 37
      main/inc/lib/CourseChatUtils.php
  23. 14 15
      main/inc/lib/TicketManager.php
  24. 40 350
      main/inc/lib/api.lib.php
  25. 20 12
      main/inc/lib/course.lib.php
  26. 2 2
      main/inc/lib/dashboard.lib.php
  27. 1 1
      main/inc/lib/document.lib.php
  28. 10 11
      main/inc/lib/grade_model.lib.php
  29. 2 1
      main/inc/lib/online.inc.php
  30. 22 0
      main/inc/lib/pear/HTML/QuickForm/Rule/MinText.php
  31. 2 2
      main/inc/lib/pear/HTML/QuickForm/RuleRegistry.php
  32. 9 9
      main/inc/lib/search/tool_processors/document_processor.class.php
  33. 19 12
      main/inc/lib/search/tool_processors/learnpath_processor.class.php
  34. 9 11
      main/inc/lib/search/tool_processors/link_processor.class.php
  35. 7 9
      main/inc/lib/search/tool_processors/quiz_processor.class.php
  36. 2 2
      main/inc/lib/search/tool_processors/search_processor.class.php
  37. 8 2
      main/inc/lib/sessionmanager.lib.php
  38. 12 6
      main/inc/lib/template.lib.php
  39. 1 0
      main/inc/lib/usermanager.lib.php
  40. 28 14
      main/inc/lib/userportal.lib.php
  41. 4 0
      main/install/configuration.dist.php
  42. 3 11
      main/install/index.php
  43. 2 8
      main/install/install.lib.php
  44. 48 0
      main/lang/brazilian/trad4all.inc.php
  45. 6 1
      main/lang/english/trad4all.inc.php
  46. 5 0
      main/lang/french/trad4all.inc.php
  47. 56 0
      main/lang/galician/trad4all.inc.php
  48. 11 0
      main/lang/slovak/trad4all.inc.php
  49. 6 1
      main/lang/spanish/trad4all.inc.php
  50. 62 13
      main/lp/learnpath.class.php
  51. 3 3
      main/lp/lp_view.php
  52. 7 3
      main/mySpace/lp_tracking.php
  53. 11 15
      main/mySpace/myStudents.php
  54. 7 6
      main/social/group_waiting_list.php
  55. 0 6
      main/template/default/chat/chat.tpl
  56. 3 0
      main/template/default/layout/menu.tpl
  57. 4 2
      main/template/default/social/whoisonline.tpl
  58. 1 1
      main/template/rainbow/gradebook/custom_certificate.tpl
  59. 1 1
      main/template/rainbow/layout/layout_2_col.tpl
  60. 5 3
      plugin/bbb/admin.php
  61. 23 17
      plugin/bbb/lib/bbb.lib.php
  62. 0 2
      plugin/bbb/start.php
  63. 1 0
      src/Chamilo/CoreBundle/Entity/HookCall.php
  64. 1 0
      src/Chamilo/CoreBundle/Entity/HookEvent.php
  65. 1 0
      src/Chamilo/CoreBundle/Entity/HookObserver.php
  66. 1 0
      src/Chamilo/CoreBundle/Entity/Language.php
  67. 3 2
      src/Chamilo/CoreBundle/Entity/Legal.php
  68. 10 11
      src/Chamilo/CoreBundle/Entity/Message.php
  69. 1 0
      src/Chamilo/CoreBundle/Entity/MessageAttachment.php
  70. 1 0
      src/Chamilo/CoreBundle/Entity/Notification.php
  71. 1 0
      src/Chamilo/CoreBundle/Entity/OpenidAssociation.php
  72. 57 0
      src/Chamilo/CourseBundle/Entity/CQuiz.php
  73. 4 4
      src/Chamilo/CourseBundle/Entity/CStudentPublicationRelUser.php
  74. 0 42
      tests/main/inc/lib/main_api.lib.test.php
  75. 14 14
      whoisonline.php

+ 3 - 0
.htaccess

@@ -52,3 +52,6 @@ RewriteRule ^badge/(\d{1,})/user/(\d{1,}) main/badge/issued_all.php?skill=$1&use
 RewriteRule ^main/exercice/(.*)$ main/exercise/$1 [QSA,L]
 # Support old URLs using the newscorm folder rather than lp
 RewriteRule ^main/newscorm/(.*)$ main/lp/$1 [QSA,L]
+
+# Deny access
+RewriteRule ^(tests|.git) - [F,L,NC]

+ 30 - 3
app/Migrations/Schema/V111/Version111.php

@@ -20,6 +20,7 @@ class Version111 extends AbstractMigrationChamilo
      */
     public function up(Schema $schema)
     {
+        $this->addSql("ALTER TABLE extra_field ENGINE=InnoDB");
         $this->addSql('CREATE TABLE extra_field_saved_search (id INT AUTO_INCREMENT NOT NULL, field_id INT DEFAULT NULL, user_id INT DEFAULT NULL, value LONGTEXT DEFAULT NULL COLLATE utf8_unicode_ci, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, INDEX IDX_16ABE32A443707B0 (field_id), INDEX IDX_16ABE32AA76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
         $this->addSql('ALTER TABLE extra_field_saved_search ADD CONSTRAINT FK_16ABE32A443707B0 FOREIGN KEY (field_id) REFERENCES extra_field (id)');
         $this->addSql('ALTER TABLE extra_field_saved_search ADD CONSTRAINT FK_16ABE32AA76ED395 FOREIGN KEY (user_id) REFERENCES user (id)');
@@ -46,7 +47,7 @@ class Version111 extends AbstractMigrationChamilo
         $this->addSql("ALTER TABLE c_forum_post ADD status INT DEFAULT NULL");
         $this->addSql("CREATE TABLE c_quiz_rel_category (iid BIGINT AUTO_INCREMENT NOT NULL, c_id INT NOT NULL, category_id INT NOT NULL, exercise_id INT NOT NULL, count_questions INT NOT NULL, PRIMARY KEY(iid))");
         $this->addSql("ALTER TABLE c_quiz ADD COLUMN question_selection_type INT");
-
+        $this->addSql("ALTER TABLE c_quiz ADD hide_question_title TINYINT(1) DEFAULT 0");
         $this->addSql("CREATE TABLE faq_question_translation (id INT AUTO_INCREMENT NOT NULL, translatable_id INT DEFAULT NULL, headline VARCHAR(255) NOT NULL, body LONGTEXT DEFAULT NULL, slug VARCHAR(50) NOT NULL, locale VARCHAR(255) NOT NULL, INDEX IDX_C2D1A2C2AC5D3 (translatable_id), UNIQUE INDEX faq_question_translation_unique_translation (translatable_id, locale), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;");
         $this->addSql("CREATE TABLE faq_category_translation (id INT AUTO_INCREMENT NOT NULL, translatable_id INT DEFAULT NULL, headline VARCHAR(255) NOT NULL, body LONGTEXT DEFAULT NULL, slug VARCHAR(50) NOT NULL, locale VARCHAR(255) NOT NULL, INDEX IDX_5493B0FC2C2AC5D3 (translatable_id), UNIQUE INDEX faq_category_translation_unique_translation (translatable_id, locale), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;");
         $this->addSql("CREATE TABLE faq_category (id INT AUTO_INCREMENT NOT NULL, rank INT NOT NULL, is_active TINYINT(1) NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, INDEX is_active_idx (is_active), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;");
@@ -109,6 +110,7 @@ class Version111 extends AbstractMigrationChamilo
         $this->addSql('UPDATE sys_calendar SET start_date = NULL WHERE start_date = "0000-00-00 00:00:00"');
         $this->addSql('UPDATE sys_calendar SET end_date = NULL WHERE end_date = "0000-00-00 00:00:00"');
 
+        $this->addSql('ALTER TABLE message ADD votes INT DEFAULT NULL');
         $this->addSql('ALTER TABLE message CHANGE update_date update_date DATETIME');
         $this->addSql('UPDATE message SET update_date = NULL WHERE update_date = "0000-00-00 00:00:00"');
 
@@ -141,6 +143,7 @@ class Version111 extends AbstractMigrationChamilo
         $this->addSql('DROP TABLE groups');
 
         if ($schema->hasTable('plugin_ticket_ticket')) {
+            // Mean plugin was installed
             $this->addSql('ALTER TABLE plugin_ticket_ticket ADD COLUMN subject varchar(255) DEFAULT NULL;');
             $this->addSql('ALTER TABLE plugin_ticket_ticket ADD COLUMN message text NOT NULL;');
             $this->addSql('UPDATE plugin_ticket_ticket t INNER JOIN plugin_ticket_message as m  ON(t.ticket_id = m.ticket_id and message_id =1)  SET t.subject = m.subject');
@@ -166,7 +169,6 @@ class Version111 extends AbstractMigrationChamilo
             $this->addSql('ALTER TABLE ticket_status DROP status_id');
             $this->addSql('ALTER TABLE ticket_category_rel_user CHANGE id id INT AUTO_INCREMENT NOT NULL, CHANGE category_id category_id INT DEFAULT NULL, CHANGE user_id user_id INT DEFAULT NULL;');
 
-
             $this->addSql('UPDATE ticket_category SET sys_insert_user_id = 1 WHERE sys_insert_user_id IS NULL');
             $this->addSql('UPDATE ticket_category SET sys_insert_datetime = NOW() WHERE sys_insert_datetime IS NULL');
             $this->addSql('UPDATE ticket_category SET course_required = 0 WHERE course_required IS NULL OR course_required = ""');
@@ -204,7 +206,6 @@ class Version111 extends AbstractMigrationChamilo
 
             $this->addSql('ALTER TABLE ticket_priority DROP priority_id, DROP priority, DROP priority_desc, DROP priority_color, DROP priority_urgency');
 
-
             $this->addSql('ALTER TABLE ticket_ticket MODIFY ticket_id INT UNSIGNED NOT NULL;');
             $this->addSql('DROP INDEX UN_ticket_code ON ticket_ticket;');
             $this->addSql('DROP INDEX FK_ticket_category ON ticket_ticket;');
@@ -259,6 +260,32 @@ class Version111 extends AbstractMigrationChamilo
 
             $this->addSql('ALTER TABLE ticket_message_attachments ADD CONSTRAINT FK_70BF9E26537A1329 FOREIGN KEY (message_id) REFERENCES ticket_message (id);');
             $this->addSql('DELETE FROM settings_current WHERE title = "Ticket"');
+        } else {
+            // Plugin was never installed. Create ticket tables
+            $this->addSql('CREATE TABLE IF NOT EXISTS ticket_project (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, other_area INT DEFAULT NULL, sys_insert_user_id INT NOT NULL, sys_insert_datetime DATETIME NOT NULL, sys_lastedit_user_id INT DEFAULT NULL, sys_lastedit_datetime DATETIME DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
+            $this->addSql('CREATE TABLE IF NOT EXISTS ticket_status (id INT AUTO_INCREMENT NOT NULL, code VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
+            $this->addSql('CREATE TABLE IF NOT EXISTS ticket_category_rel_user (id INT AUTO_INCREMENT NOT NULL, category_id INT DEFAULT NULL, user_id INT DEFAULT NULL, INDEX IDX_5B8A98712469DE2 (category_id), INDEX IDX_5B8A987A76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
+            $this->addSql('CREATE TABLE IF NOT EXISTS ticket_message_attachments (id INT AUTO_INCREMENT NOT NULL, ticket_id INT DEFAULT NULL, message_id INT DEFAULT NULL, path VARCHAR(255) NOT NULL, filename LONGTEXT NOT NULL, size INT NOT NULL, sys_insert_user_id INT NOT NULL, sys_insert_datetime DATETIME NOT NULL, sys_lastedit_user_id INT DEFAULT NULL, sys_lastedit_datetime DATETIME DEFAULT NULL, INDEX IDX_70BF9E26700047D2 (ticket_id), INDEX IDX_70BF9E26537A1329 (message_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
+            $this->addSql('CREATE TABLE IF NOT EXISTS ticket_priority (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, color VARCHAR(255) NOT NULL, urgency VARCHAR(255) NOT NULL, sys_insert_user_id INT NOT NULL, sys_insert_datetime DATETIME NOT NULL, sys_lastedit_user_id INT DEFAULT NULL, sys_lastedit_datetime DATETIME DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
+            $this->addSql('CREATE TABLE IF NOT EXISTS ticket_message (id INT AUTO_INCREMENT NOT NULL, ticket_id INT DEFAULT NULL, subject VARCHAR(255) DEFAULT NULL, message LONGTEXT DEFAULT NULL, status VARCHAR(255) NOT NULL, ip_address VARCHAR(255) NOT NULL, sys_insert_user_id INT NOT NULL, sys_insert_datetime DATETIME NOT NULL, sys_lastedit_user_id INT DEFAULT NULL, sys_lastedit_datetime DATETIME DEFAULT NULL, INDEX IDX_BA71692D700047D2 (ticket_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
+            $this->addSql('CREATE TABLE IF NOT EXISTS ticket_category (id INT AUTO_INCREMENT NOT NULL, project_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, total_tickets INT NOT NULL, course_required TINYINT(1) NOT NULL, sys_insert_user_id INT NOT NULL, sys_insert_datetime DATETIME NOT NULL, sys_lastedit_user_id INT DEFAULT NULL, sys_lastedit_datetime DATETIME DEFAULT NULL, INDEX IDX_8325E540166D1F9C (project_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
+            $this->addSql('CREATE TABLE IF NOT EXISTS ticket_ticket (id INT AUTO_INCREMENT NOT NULL, project_id INT DEFAULT NULL, category_id INT DEFAULT NULL, priority_id INT DEFAULT NULL, course_id INT DEFAULT NULL, session_id INT DEFAULT NULL, status_id INT DEFAULT NULL, code VARCHAR(255) NOT NULL, subject VARCHAR(255) NOT NULL, message LONGTEXT DEFAULT NULL, personal_email VARCHAR(255) NOT NULL, assigned_last_user INT DEFAULT NULL, total_messages INT NOT NULL, keyword VARCHAR(255) DEFAULT NULL, source VARCHAR(255) DEFAULT NULL, start_date DATETIME DEFAULT NULL, end_date DATETIME DEFAULT NULL, sys_insert_user_id INT NOT NULL, sys_insert_datetime DATETIME NOT NULL, sys_lastedit_user_id INT DEFAULT NULL, sys_lastedit_datetime DATETIME DEFAULT NULL, INDEX IDX_EDE2C768166D1F9C (project_id), INDEX IDX_EDE2C76812469DE2 (category_id), INDEX IDX_EDE2C768497B19F9 (priority_id), INDEX IDX_EDE2C768591CC992 (course_id), INDEX IDX_EDE2C768613FECDF (session_id), INDEX IDX_EDE2C7686BF700BD (status_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
+            $this->addSql('CREATE TABLE IF NOT EXISTS ticket_assigned_log (id INT AUTO_INCREMENT NOT NULL, ticket_id INT DEFAULT NULL, user_id INT DEFAULT NULL, sys_insert_user_id INT NOT NULL, assigned_date DATETIME NOT NULL, INDEX IDX_54B65868700047D2 (ticket_id), INDEX IDX_54B65868A76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
+
+            $this->addSql('ALTER TABLE ticket_category_rel_user ADD CONSTRAINT FK_5B8A98712469DE2 FOREIGN KEY (category_id) REFERENCES ticket_category (id)');
+            $this->addSql('ALTER TABLE ticket_category_rel_user ADD CONSTRAINT FK_5B8A987A76ED395 FOREIGN KEY (user_id) REFERENCES user (id)');
+            $this->addSql('ALTER TABLE ticket_message_attachments ADD CONSTRAINT FK_70BF9E26700047D2 FOREIGN KEY (ticket_id) REFERENCES ticket_ticket (id)');
+            $this->addSql('ALTER TABLE ticket_message_attachments ADD CONSTRAINT FK_70BF9E26537A1329 FOREIGN KEY (message_id) REFERENCES ticket_message (id)');
+            $this->addSql('ALTER TABLE ticket_message ADD CONSTRAINT FK_BA71692D700047D2 FOREIGN KEY (ticket_id) REFERENCES ticket_ticket (id)');
+            $this->addSql('ALTER TABLE ticket_category ADD CONSTRAINT FK_8325E540166D1F9C FOREIGN KEY (project_id) REFERENCES ticket_project (id)');
+            $this->addSql('ALTER TABLE ticket_ticket ADD CONSTRAINT FK_EDE2C768166D1F9C FOREIGN KEY (project_id) REFERENCES ticket_project (id)');
+            $this->addSql('ALTER TABLE ticket_ticket ADD CONSTRAINT FK_EDE2C76812469DE2 FOREIGN KEY (category_id) REFERENCES ticket_category (id)');
+            $this->addSql('ALTER TABLE ticket_ticket ADD CONSTRAINT FK_EDE2C768497B19F9 FOREIGN KEY (priority_id) REFERENCES ticket_priority (id)');
+            $this->addSql('ALTER TABLE ticket_ticket ADD CONSTRAINT FK_EDE2C768591CC992 FOREIGN KEY (course_id) REFERENCES course (id)');
+            $this->addSql('ALTER TABLE ticket_ticket ADD CONSTRAINT FK_EDE2C768613FECDF FOREIGN KEY (session_id) REFERENCES session (id)');
+            $this->addSql('ALTER TABLE ticket_ticket ADD CONSTRAINT FK_EDE2C7686BF700BD FOREIGN KEY (status_id) REFERENCES ticket_status (id)');
+            $this->addSql('ALTER TABLE ticket_assigned_log ADD CONSTRAINT FK_54B65868700047D2 FOREIGN KEY (ticket_id) REFERENCES ticket_ticket (id)');
+            $this->addSql('ALTER TABLE ticket_assigned_log ADD CONSTRAINT FK_54B65868A76ED395 FOREIGN KEY (user_id) REFERENCES user (id)');
         }
 
         $this->addSql("INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES ('ticket_allow_student_add', NULL, 'radio','Ticket', 'false','TicketAllowStudentAddTitle','TicketAllowStudentAddComment',NULL,NULL, 0)");

+ 4 - 4
app/Migrations/Schema/V111/Version20160330103045.php

@@ -155,10 +155,10 @@ class Version20160330103045 extends AbstractMigrationChamilo
         $sql = "UPDATE user SET username_canonical = username";
         $this->addSql($sql);
 
-        /*$this->addSql("CREATE TABLE fos_user_user_group (user_id INT NOT NULL, group_id INT NOT NULL, INDEX IDX_B3C77447A76ED395 (user_id), INDEX IDX_B3C77447FE54D947 (group_id), PRIMARY KEY(user_id, group_id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;");
-        $this->addSql("CREATE TABLE fos_group (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, roles LONGTEXT NOT NULL COMMENT '(DC2Type:array)', code VARCHAR(40) NOT NULL, UNIQUE INDEX UNIQ_4B019DDB5E237E06 (name), UNIQUE INDEX UNIQ_4B019DDB77153098 (code), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;");
-        $this->addSql("ALTER TABLE fos_user_user_group ADD CONSTRAINT FK_B3C77447A76ED395 FOREIGN KEY (user_id) REFERENCES user (id);");
-        $this->addSql("ALTER TABLE fos_user_user_group ADD CONSTRAINT FK_B3C77447FE54D947 FOREIGN KEY (group_id) REFERENCES fos_group (id);");*/
+        $this->addSql("CREATE TABLE fos_group(id INT AUTO_INCREMENT NOT NULL, code VARCHAR(40) NOT NULL, UNIQUE INDEX UNIQ_4B019DDB77153098 (code), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB");
+        $this->addSql("CREATE TABLE fos_user_user_group (user_id INT NOT NULL, group_id INT NOT NULL, INDEX IDX_B3C77447A76ED395 (user_id), INDEX IDX_B3C77447FE54D947 (group_id), PRIMARY KEY(user_id, group_id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB");
+        $this->addSql("ALTER TABLE fos_user_user_group ADD CONSTRAINT FK_B3C77447A76ED395 FOREIGN KEY (user_id) REFERENCES user (id)");
+        $this->addSql("ALTER TABLE fos_user_user_group ADD CONSTRAINT FK_B3C77447FE54D947 FOREIGN KEY (group_id) REFERENCES fos_group (id)");
     }
 
     /**

+ 3 - 1
app/Migrations/Schema/V111/Version20160930144400.php

@@ -29,7 +29,9 @@ class Version20160930144400 extends AbstractMigrationChamilo
             )
         ');
 
-        $this->addSql('ALTER TABLE extra_field ADD visible_to_others TINYINT(1) DEFAULT 0, CHANGE visible visible_to_self TINYINT(1) DEFAULT 0');
+        $this->addSql(
+            'ALTER TABLE extra_field ADD visible_to_others TINYINT(1) DEFAULT 0, CHANGE visible visible_to_self TINYINT(1) DEFAULT 0'
+        );
     }
 
     /**

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

@@ -5306,6 +5306,7 @@ div#chat-remote-video video {
 }
 #whoisonline .items-user-avatar .img-responsive{
     display: inline-block;
+    height: 128px;
 }
 #whoisonline .items-user-name{
   font-size: 12px;

+ 8 - 0
app/Resources/public/css/themes/rainbow/default.css

@@ -68,6 +68,14 @@ a:focus {
 #session-block .list-group .list-group-item{
     float: left;
 }
+#courseList .list-group .title{
+    font-size: 12px;
+    font-weight: normal;
+    display: inline !important;
+}
+#course-block .list-group .list-group-item{
+    float: left;
+}
 .title{
     position: static;
     font-size: 25px;

+ 72 - 4
documentation/changelog.html

@@ -47,9 +47,68 @@
 <p><i>Note: most #wxyz references are issue numbers you can find in <a href="http://support.chamilo.org/projects/chamilo-18/issues" target="_blank">our public bug tracking system</a>. Some references marked BT#xyz are developments made externally for BeezNest customers and integrated into Chamilo. The details of these tasks cannot be seen for confidentiality reasons, but the code change is public and can be reviewed by anyone.</i></p>
 <p>&nbsp;</p>
 
+    <a name="1.11.2"></a>
+
+    <h1>Chamilo 1.11.2 - , 28th of October 2016</h1>
+    <h3>Release notes - summary</h3>
+    <p>Chamilo 1.11.2 is a minor, bugfix release of the 1.11.x branch, with a few bugfixes on top of 1.11.0.</p>
+    <h3>Release name</h3>
+    <p><a href=""></a> .</p>
+    <h3>Security fixes</h3>
+    <ul>
+        <li>Several security fixes were applied to this version. Please update as soon as possible. You can find details of the vulnerability on <a href="https://support.chamilo.org/projects/chamilo-18/wiki/Security_issues">Chamilo's security page</a></li>
+    </ul>
+    <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>
+    <p>None in this version.</p>
+    <h4>For developers and sysadmins</h4>
+    <ul>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/641c865ad7b6c0623fe2f0c92e946fcbbd2e93de">641c865a</a>) Add configuration block_quiz_mail_notification_general_coach</li>
+    </ul>
+    <h3>Improvements (minor features) and debug</h3>
+    <ul>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/7055a50530d1b5049d03def4248555f6b8b1d191">7055a505</a> - <a href="https://task.beeznest.com/issues/11636">BT#11636</a>) Show participants emails on videoconference admin view</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a3feab412d34e0254cb60c3f8a02e059479e76f8">a3feab41</a> - <a href="https://task.beeznest.com/issues/11636">BT#11636</a>) Fix show links for videoconference</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/e6887301cbedc0bc1353d95450c7e303c5d74240">e6887301</a>) Restoring missing "database" and "language" notes in installation guide</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/bc319badbff4b425595d815c2b6066f1ec2fb501">bc319bad</a> - <a href="https://task.beeznest.com/issues/11860">BT#11860</a>) Fix teacher can't access LP</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/8f40f7d803dbc595abdebd0c4f76fe7185257bbd">8f40f7d8</a> - <a href="https://task.beeznest.com/issues/11879">BT#11879</a>) Lp build: Load forums as in the forum tool</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a12383d866b718499f73e79a996a532afc0d6b5c">a12383d8</a>) Add user object in tpl $tplContent->assign('new_user', $user);</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/fe6864713a079c8415261635cf03bce23a75ceb2">fe686471</a> - <a href="https://task.beeznest.com/issues/11890">BT#11890</a>) Add new config hide_my_certificate_link</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/f6a1adb81323fa82a174d1455b886c1577b813e7">f6a1adb8</a>) Add missing fos_group* tables on upgrade from 1.10</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/938c4ed30a5a950348cd0dbf1147c9c91cabf427">938c4ed3</a>) Add missing database changes on upgrade from 1.10 on message.votes column and ticket tables</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/57cfd396830f117506035316bba44567a78babcd">57cfd396</a>) Update upgrade section in installation guide.</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/660c2efb0e9819b4efc98f269f1d8fe2c8fca7a9">660c2efb</a> - <a href="https://task.beeznest.com/issues/11770">BT#11770</a>) If $showInvisibleFiles = false then hide documents</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/3c604d8b7ae700ecc3ecfa190948d312f965213b">3c604d8b</a>) Exercise draggable question type fix from 1.10</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/07db02b7498fa36bb745696fedcdf04881b2c63c">07db02b7</a> - <a href="https://task.beeznest.com/issues/11869">BT#11869</a>) Fix whoisonline page</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/f3f1b6e1dc46450c099b751a8857ff618829c8e1">f3f1b6e1</a>) Fix hide_question_title DB errors</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/0f4c7f8603d9d5f7e7095812913bf85b01bb0d49">0f4c7f86</a> - <a href="https://task.beeznest.com/issues/11859">BT#11859</a>) Fix "Show expected choice in last attempt" option in exercises</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/9795d9f3fa73d22eefc52b9143ef76be401db8fb">9795d9f3</a>) Fix RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT conditions</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/269d392f2c26d124faf8c7c1919dfc97632b0f66">269d392f</a>) Fix visible_to_self query in document.lib.php</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/0ab33541864b5e0e8ac93b2ad2ba5298f258e9fc">0ab33541</a>) Block access to tests and .git via browser in .htaccess</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/e82597613a26dc88f143b36825b3863275c156f2">e8259761</a>) Partially undo change. Report to pdf is available.</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/9e7d2c6c5ee9fba00d4c1f437921a2a2ed64023c">9e7d2c6c</a>) Hide pdf link depending of setting hide_certificate_export_link</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/a0665ba22eab4dde9daf7bf6f68c0dc003fce45c">a0665ba2</a>) Fix migration errors with extra_field table type</li>
+    </ul>
+    <h3>Stylesheets and theming</h3>
+    <p>None in this version.</p>
+    <h3>Web services</h3>
+    <p>None in this version.</p>
+    <h3>Removals</h3>
+    <p>None in this version.</p>
+    <h3>Known issues</h3>
+    <ul>
+        <li>IMS/QTI import/export has minor 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.8. 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 Xapian search module is broken. We'll fix that soon, but not a lot of people use it</li>
+        <li>Encoding issues when importing accentuated characters from MS-Office-generated .xlsx files (for example when importing exercises). This isn't really a Chamilo issue, but since some users might have issues with that, we've decided to report it to avoid any surprise.</li>
+    </ul>
+
+
     <a name="1.11.0"></a>
 
-    <h1>Chamilo 1.11.0 - Lecce, 15th of October 2016</h1>
+    <h1>Chamilo 1.11.0 - Lecce, 17th of October 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,7 +118,7 @@
     <h3>Possibly breaking changes</h3>
     <p>None in this version.</p>
     <h3>Notable new Features</h3>
-    <h4>For end-users, teachers and Chamilo admins (by relevance)</h4>
+    <h4>For end-users, teachers and Chamilo admins (by date)</h4>
     <ul>
         <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>
@@ -83,7 +142,7 @@
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/d01d9b71b20161b7645e46365418213a8292130d">d01d9b71</a> - <a href="https://task.beeznest.com/issues/11642">BT#11642</a>) Add links sorting</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/0430b64579ec67e89ffe2216148b070322f4eb3e">0430b645</a> - <a href="https://task.beeznest.com/issues/11486">BT#11486</a>) Add custom logo to pdf template</li>
     </ul>
-    <h4>For developers and sysadmins (by relevance)</h4>
+    <h4>For developers and sysadmins (by date)</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>
@@ -150,9 +209,16 @@
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/0dfdd23a2ca0268034e05eae745027218974768a">0dfdd23a</a> - <a href="https://support.chamilo.org/issues/11192">#11192</a>) Add function to check if the current lp item is first, both, last or none from lp list</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/3c8e000579ab57ac165816e90175dd260d229593">3c8e0005</a>) Add function api_get_css_asset() to get css files from web/assets</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/23513de43692dd98a690cf2835d12112a1e8cdad">23513de4</a> - <a href="https://support.chamilo.org/issues/8378">CT#8378</a>) Improve exercise list performance</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/879d1ffefe193d20604857fbb72091470151ff35">879d1ffe</a> - <a href="https://task.beeznest.com/issues/11194">BT#11194</a>) Add support for new custom frozen form element template</li>
     </ul>
     <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/ea6a6863895e7388a7cb5cff223cb15ede01fc95">ea6a6863</a>) Remove double "freeze" call in create_user() form</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/3df995697e387723fd0530bc0ed3a1e2aa8d35e4">3df99569</a>) Use CREATE TABLE IF NOT EXISTS in v1.10 migration script on c_lp_category</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/7eedec36d4cb2aa41e939ab4636feaa0f7010e3b">7eedec36</a>) Fix v1.10 migration queries to avoid empty insert_user_id in c_item_property</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/53af300a9d920101d3989661d852ce498d7bfcb0">53af300a</a>) Fix v1.10 migration query to avoid empty session coach ID</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/e840d1e6894fafb3ba3bf255a886002234446117">e840d1e6</a>) Fix use of admin id in upgrader (further fixes to ensure admin user is active)</li>
+        <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/c75fa66bee6dc384fdca6469ca46029412c609bb">c75fa66b</a>) Fix tickets: fatal error, lang vars, format code</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/8f218b9ea6b7018930826955a3832b7ffb43ac7b">8f218b9e</a>) Fix Upload student work if you upload the same filename that previously was deleted</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/dff94509a60de3057d2cdef20bbde2bd0cfa09b4">dff94509</a>) Fix delete session if exist an assignments inside</li>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/17aca502d1e3c1cb6806f40737fbb4f7edfa19d4">17aca502</a>) Allow update session dates when these are null</li>
@@ -1457,6 +1523,7 @@
     <h3>Stylesheets and theming</h3>
     <ul>
         <li>(<a href="https://github.com/chamilo/chamilo-lms/commit/e4239dd983fac3ef9092aaa53779cc0f0b56bfec">e4239dd9</a>) Add CSS theme for Holi indian festival, contribution by official provider contidosdixitais.com</li>
+        <li>Many changes have been made to the default template at main/template/default/, so if you have derived a custom template from it in previous versions, you will have to work on the basis of the default again, most probably.</li>
     </ul>
     <h3>Files structure</h3>
     <ul>
@@ -1467,7 +1534,8 @@
     </ul>
     <h3>Web services</h3>
     <ul>
-        <li>Added webservices to configure the visibility of courses for specific users in the courses catalogue</li>
+        <li>Added web services to configure the visibility of courses for specific users in the courses catalogue</li>
+        <li>Added REST web services to communicate with the official mobile app</li>
     </ul>
     <h3>Removals</h3>
     <ul>

+ 51 - 59
documentation/installation_guide.html

@@ -7,8 +7,7 @@
     <link rel="shortcut icon" href="../favicon.ico" type="image/x-icon" />
     <link href="js/prettify.css" type="text/css" rel="stylesheet" />
     <script src="js/prettify.js" type="text/javascript"></script>
-
-    </head>
+</head>
 <body>
 <nav class="navbar navbar-default navbar-fixed-top">
     <div class="container">
@@ -21,7 +20,6 @@
             </button>
             <a class="navbar-brand" href="#">Chamilo - Documentation</a>
         </div>
-
         <div class="collapse navbar-collapse">
             <ul class="nav navbar-nav">
                 <li class="active"><a href="index.html">Home</a></li>
@@ -47,14 +45,11 @@
 <p>French: Vous pouvez aussi lire <a href="installation_guide_fr_FR.html">ce guide d'installation en français</a>.</p>
 <p>Italian: You can also read <a href="installation_guide_it_IT.html">this guide in Italian</a>.</p>
 
-
 <h2><b>Contents</b></h2>
-
 <ol>
-
   <li><a href="#1._Pre-requisites">Pre-requisites</a></li>
   <li><a href="#2._Installation_of_Chamilo_LMS">Installation of Chamilo LMS</a></li>
-  <li><a href="#3._Upgrade_from_a_previous_version_of">Upgrade from a previous version of Chamilo/Dok€os</a></li>
+  <li><a href="#3._Upgrade">Upgrade</a></li>
   <li><a href="#4._Troubleshooting">Troubleshooting</a></li>
   <li><a href="#5._Administration_section">Administration section</a></li>
   <li><a href="#6._LDAP">LDAP&nbsp;</a></li>
@@ -87,41 +82,29 @@ Chamilo is mainly a LMS running (the so called <span style="font-weight: bold;">
 <li><span style="font-weight: bold;">PHP 5.4+ (5.6 recommended for greater efficiency) (support for PHP7 seems OK but not evaluated on high load setups just yet)</span></li>
 </ul>
 <br />
-
-All these pieces of software are free software and freely available. <br />
-
-<br />
-
+All these pieces of software are free software and freely available. <br /><br />
 To run Chamilo <span style="font-weight: bold;">LMS</span> on your server, you need to install WAMP, LAMP or MAMP:
 <ul>
-
   <li>To install <span style="font-weight: bold;">WAMP</span> (AMP on Windows), we recommend the <a href="http://www.apachefriends.org/en/xampp.html">XAMPP</a> .exe installer<a href="http://www.apachefriends.org/en/xampp.html"></a></li>
-
   <li>To install <span style="font-weight: bold;">LAMP</span>
 (AMP on Linux), use the Package manager of your favourite distribution (Synaptic, RPMFinder etc.).
 For instance, on a Ubuntu server,&nbsp;use Shell or Synaptic following the
-    <a href="http://ubuntuguide.org/wiki/Ubuntu:Feisty#Apache_HTTP_Server">Ubuntuguide on Apache</a> and the following sections</li>
-
+    <a href="http://ubuntuguide.org/wiki/Ubuntu:Feisty#Apache_HTTP_Server">Ubuntuguide on Apache</a> and the following sections
+  </li>
   <li>To install <span style="font-weight: bold;">MAMP</span> (AMP on Mac OS X), refer to the <a href="http://www.mamp.info/en/index.php">MAMP</a> dedicated website<a href="http://www.mamp.info/en/index.php"></a></li>
   <li>Make sure you check the <a href="dependencies.html" title="Dependencies">Dependencies page</a> if you need to know the requirements in more precise way.
 </ul>
-
-
 <br />
 
-
 <h3><span style="font-weight: bold;">MySQL or MariaDB database server</span></h3>
-
 You will need a login and password allowing you to manage and create a database. Usually, the default configuration on local computers is
 to allow you to connect as root with an empty password. It is <b>HIGHLY RECOMMENDED</b> to change the password and define a user with access to
 only a specific database. Please refer to the MySQL or MariaDB documentation in order to do this.<br />
 <br />
 <span class="text-muted">Chamilo versions prior to 1.9.0 required to choose between multiple databases and a deprecated single-database mode.
     This has been cleaned up and simplified to one single database.</span><br />
-
 <hr style="width: 100%; height: 2px;" />
 <h2><a name="2._Installation_of_Chamilo_LMS"></a><span style="font-weight: bold;">2. Installation of Chamilo LMS</span></h2>
-
 <p>
 Before you start installing Chamilo LMS, you must understand that, because we provide Chamilo as free software, many services providers
     have been able to develop extensions of Chamilo, which have made it possible to install Chamilo in many ways:<br />
@@ -265,58 +248,49 @@ date.timezone = 'America/New_York'
 
 
 <hr />
-<h2><a name="3._Upgrade_from_a_previous_version_of"></a>3. Upgrade from a
-previous version of Chamilo LMS (1.*) or Dok€os (&lt;2.0)</h2>
+<h2><a name="3._Upgrade"></a>3. Upgrade</h2>
 
 Before upgrading, we <b>heavily</b> recommend you do a full backup of the previous
 Chamilo directories and databases. If you are unsure how to achieve this
 please ask your hosting provider for advice <b>before</b> you continue further.<br /><br />
-<em>Chamilo LMS 1.10 comes with a new database structure in comparison to 1.9,
-as was the case between 1.9 and 1.8.
-Although the upgrade script takes the migration in charge, it might be generating
-a very heavy load on your server during the upgrade, and will change your
-database considerably, preserving the data as well as possible (we have tested
-the procedure many times, but remember this is GNU/GPLv3 and we cannot be held
-responsible for what would happen to your data without professional supervision).
-This is why we *really* recommend you take a full backup of your system before
-you upgrade.</em>
-<br />
-<br />
-Also note that Chamilo LMS 1.10 unites all language files from the
-main/lang/ directory into one single file by language.
-If you have changed language files directly (instead of
-using the recommended way: sub-languages), you should take a backup copy of
-these changed translations before you upgrade. Sublanguages might also require
-som manual work, but the upgrade procedure will not touch sublanguages directly.
-<br /><br />
+
 <div class="text-muted"> NOTE: For very heavy Chamilo databases, some of our official
 providers have developed improved migration procedures that use more memory but
 process the upgrades much faster. If this is the kind of thing you
 need, we highly recommend you contact them (see reference below).</div>
-    <br />
+<br />
 <div class="text-muted"> NOTE: This version of Chamilo can only be used to upgrade
-from earlier versions of Chamilo 1.9.*. For example, you cannot use the normal
+from earlier versions of Chamilo 1.9.*. You cannot use the normal
 upgrade scripts from Chamilo 1.10 to upgrade from Chamilo 1.8.* or even Dok€os 2.0
 (which was born after the split with Chamilo). If you need this, please contact one
 of the Chamilo Association's <a href="https://chamilo.org/providers">official providers</a>)</div>
-
 <p></p>
 
-<h3>3.1 Upgrading from Chamilo 1.10.x (minor upgrade)</h3>
-As this is only a minor version change from previous 1.10.* versions of Chamilo, the only thing you need to do is:
+<h3>3.1 Upgrading from Chamilo 1.11.x (Minor version)</h3>
+As this is only a minor version change from previous 1.11.* versions of Chamilo, the only thing you need to do is:
 <ul>
   <li> check that you haven't left any customised stylesheet or image <span class="text-muted">(if you have, make sure you keep a copy on the side*)</span></li>
-  <li> download the Chamilo 1.10 install package from the <a href="http://www.chamilo.org/download">Chamilo download page</a></li>
-  <li> unzip the new files of Chamilo 1.10 over the files of the older version
+  <li> download the Chamilo 1.11 install package from the <a href="http://www.chamilo.org/download">Chamilo download page</a></li>
+  <li> unzip the new files of Chamilo 1.11 over the files of the older version
+    (or unzip the files in one folder and then copy the files from there to the
+    older version's directory).</li>
+  <li> clean your app/cache/twig directory: delete all the contents *in* this directory (do NOT remove the directory itself, only its contents!).
+       It will all be re-generated.
+      You can also delete the contents of this directory through the "Archive directory cleanup" option in the "System" box of the Administration page.</li>
+  <li> you're done! No other upgrade procedure is required</li>
+</ul>
+<h3>3.2 Upgrading from Chamilo 1.10.x</h3>
+<ul>
+  <li> check that you haven't left any customised stylesheet or image <span class="text-muted">(if you have, make sure you keep a copy on the side*)</span></li>
+  <li> download the Chamilo 1.11 install package from the <a href="http://www.chamilo.org/download">Chamilo download page</a></li>
+  <li> unzip the new files of Chamilo 1.11 over the files of the older version
     (or unzip the files in one folder and then copy the files from there to the
     older version's directory). Note that you should remove the "home" and
     "searchdb" directories from the package before you overwrite the previous
     files.</li>
-  <li> edit the app/config/configuration.php file: at the en of the file, locate the previous version number (e.g. '1.10.4') and change it to this new version (e.g. '1.10.8')</li>
   <li> clean your app/cache/twig directory: delete all the contents *in* this directory (do NOT remove the directory itself, only its contents!). It will all be re-generated. You can also delete the contents of this directory through the "Archive directory cleanup" option in the "System" box of the Administration page.</li>
-  <li> you're done! No other upgrade procedure is required</li>
+  <li>Go to your portal install page (main/install/) and follow the upgrade procedure</li>
 </ul>
-
 Please note that if you (unluckily) upgraded from any of the 1.9 versions to 1.10.0 against our recommendations, we
     provide a patch script that should get most of it in order. The script is available here:<br />
     <a href="https://raw.githubusercontent.com/chamilo/chamilo-lms/1.10.x/tests/scripts/fix_migrations_1.9.x_1.10.0.php">https://raw.githubusercontent.com/chamilo/chamilo-lms/1.10.x/tests/scripts/fix_migrations_1.9.x_1.10.0.php</a> or, if
@@ -326,8 +300,26 @@ Please note that if you (unluckily) upgraded from any of the 1.9 versions to 1.1
     This script is provided without warranty. Please *always* take a backup before you use it.
 <br />
 
+<h3>3.3 Upgrading from Chamilo 1.9.x</h3>
+
+<p>Chamilo LMS 1.11.x comes with a new database structure in comparison to 1.9,
+as was the case between 1.9 and 1.8.
+Although the upgrade script takes the migration in charge, it might be generating
+a very heavy load on your server during the upgrade, and will change your
+database considerably, preserving the data as well as possible (we have tested
+the procedure many times, but remember this is GNU/GPLv3 and we cannot be held
+responsible for what would happen to your data without professional supervision).
+This is why we *really* recommend you take a full backup of your system before
+you upgrade.</p>
+
+Also note that Chamilo LMS 1.10 unites all language files from the
+main/lang/ directory into one single file by language.
+If you have changed language files directly (instead of
+using the recommended way: sub-languages), you should take a backup copy of
+these changed translations before you upgrade. Sublanguages might also require
+some manual work, but the upgrade procedure will not touch sublanguages directly.
+<br /><br />
 
-<h3>3.2 Upgrading from Chamilo 1.9.x</h3>
 <ul>
   <li> make sure you don't have tables from previous versions of Chamilo still hanging around. These can cause the upgrade to fail. In particular, tables from versions 1.8.* could have multiple tables for each course, resulting in many tables with the same prefix. Only tables without prefix or with a "c_" prefix exist in 1.9 and superior versions. Make sure none of these tables persist. Take a backup of your database (just in case) and delete these prefixed tables (drop table ...).</li>
   <li> check that you haven't left any customised stylesheet or image <span class="text-muted">(if you have, make sure you keep a copy on the side*)</span></li>
@@ -362,12 +354,12 @@ to the old ones which will be deprecated shortly (not maintained).</span>
 Do not delete the previous Chamilo installation directory before installing
 the new one. The upgrade process should take care of all unrequired folders.</p>
 
-    <h3>3.2 Upgrading from Chamilo 1.8.x</h3>
-    To upgrade from version of Chamilo prior to 1.9.0, you will need to first upgrade them to 1.9.10.x, then upgrade again (separately) to 1.10.x.
-    You can find the 1.9.10.x packages here: <a href="https://github.com/chamilo/chamilo-lms/releases">https://github.com/chamilo/chamilo-lms/releases</a>
+<h3>3.4 Upgrading from Chamilo 1.8.x</h3>
+To upgrade from version of Chamilo prior to 1.9.0, you will need to first upgrade them to 1.9.10.x, then upgrade again (separately) to 1.10.x.
+You can find the 1.9.10.x packages here: <a href="https://github.com/chamilo/chamilo-lms/releases">https://github.com/chamilo/chamilo-lms/releases</a>
 
-    <p></p>
-<h3>3.5 In both last cases</h3>
+<p></p>
+<h3>3.5 Directories permissions when upgrading from 1.9.x</h3>
 
 The following directories need to be readable, writeable and executable for the web server:
 <ul>
@@ -781,7 +773,7 @@ If you have issues with files taking a long time to download, make sure you reco
     <hr style="width: 100%; height: 2px;" />
     <h2><a name="17._Git_Upgrade"></a>Upgrading from Git</h2>
     <p>If you have sufficient experience with Git and have installed your initial Chamilo portal
-        from the Git version, you might want to upgrade from 1.9.x to 1.10.x using Git directly.<br />
+        from the Git version, you might want to upgrade from 1.9.x to 1.11.x using Git directly.<br />
     Here are a few tips that might help you:<br />
     <ul>
     <li>Get into your Chamilo Git repository</li>

+ 4 - 3
documentation/optimization.html

@@ -380,8 +380,8 @@ The logic behind this verification is that, whatever resources that needs to be
     (usually called download.php but there are more than one depending on the
     type of resource).<br /><br />
 If you want to speed up files accesses and you don't really care about whom can
-    see your files, then an option is to simply remove this redirection to
-    download.php and let Apache treat the file on its own.<br /><br />
+    see your files, then an option is to simply change this redirection to
+    download.php and let Apache treat the file directly.<br /><br />
 Furthermore, using a PHP script for the download (unless you have special rules)
     will usually prevent static content caching, which will multiply downloads
     and use large amount of additional bandwidth.<br /><br />
@@ -397,13 +397,14 @@ RewriteRule ([^/]+)/document/(.*)$ /main/document/download.php?doc_url=/$2&cDir=
 RewriteRule ([^/]+)/work/(.*)$ /main/work/download.php?file=work/$2&cDir=$1 [QSA,L]
 </IfModule>
 </pre><br />
-The idea is to remove the "scorm" line, so you are left with:
+The idea is to allow direct access (without access validation) to resources in the "scorm" directory with:
 <pre>
 <IfModule mod_rewrite.c>
 RewriteEngine On
 RewriteBase /courses/
 RewriteCond %{REQUEST_URI} !^/main/
 RewriteRule ([^/]+)/document/(.*)&(.*)$ $1/document/$2///$3 [N]
+RewriteRule ([^/]+)/scorm/(.*)$ /app/courses/$1/scorm/$2 [QSA,L]
 RewriteRule ([^/]+)/document/(.*)$ /main/document/download.php?doc_url=/$2&cDir=$1 [QSA,L]
 RewriteRule ([^/]+)/work/(.*)$ /main/work/download.php?file=work/$2&cDir=$1 [QSA,L]
 </IfModule>

+ 6 - 7
main/admin/grade_models.php

@@ -13,7 +13,7 @@ $this_section = SECTION_PLATFORM_ADMIN;
 api_protect_admin_script();
 
 if (api_get_setting('gradebook_enable_grade_model') != 'true') {
-    api_not_allowed();
+    api_not_allowed(true);
 }
 
 //Add the JS needed to use the jqgrid
@@ -38,8 +38,8 @@ if ($action === 'add') {
 }
 
 $htmlHeadXtra[]= '<script>
-
 function plusItem(item) {
+    console.log(item);
     if (item != 1) {
 		document.getElementById(item).style.display = "inline";
     	document.getElementById("plus-"+item).style.display = "none";
@@ -69,7 +69,6 @@ function minItem(item) {
 }
 </script>';
 
-// The header.
 Display::display_header();
 
 //jqgrid will use this URL to do the selects
@@ -113,10 +112,10 @@ $extra_params['height'] = 'auto';
 
 //With this function we can add actions to the jgrid (edit, delete, etc)
 $action_links = 'function action_formatter(cellvalue, options, rowObject) {
-                         return \'<a href="?action=edit&id=\'+options.rowId+\'">'.Display::return_icon('edit.png',get_lang('Edit'),'',ICON_SIZE_SMALL).'</a>'.
-                         '&nbsp;<a onclick="javascript:if(!confirm('."\'".addslashes(api_htmlentities(get_lang("ConfirmYourChoice"),ENT_QUOTES))."\'".')) return false;"  href="?sec_token='.$token.'&action=delete&id=\'+options.rowId+\'">'.Display::return_icon('delete.png',get_lang('Delete'),'',ICON_SIZE_SMALL).'</a>'.
-                         '\';
-                 }';
+     return \'<a href="?action=edit&id=\'+options.rowId+\'">'.Display::return_icon('edit.png',get_lang('Edit'),'',ICON_SIZE_SMALL).'</a>'.
+     '&nbsp;<a onclick="javascript:if(!confirm('."\'".addslashes(api_htmlentities(get_lang("ConfirmYourChoice"),ENT_QUOTES))."\'".')) return false;"  href="?sec_token='.$token.'&action=delete&id=\'+options.rowId+\'">'.Display::return_icon('delete.png',get_lang('Delete'),'',ICON_SIZE_SMALL).'</a>'.
+     '\';
+}';
 ?>
 <script>
 $(function() {

+ 0 - 12
main/admin/user_list.php

@@ -440,18 +440,6 @@ function get_number_of_users()
 function get_user_data($from, $number_of_items, $column, $direction)
 {
     $sql = prepare_user_sql_query(false);
-
-    /* @todo will not work because now we use the salt field
-    $checkPassStrength = isset($_GET['check_easy_passwords']) && $_GET['check_easy_passwords'] == 1 ? true : false;
-    if ($checkPassStrength) {
-        $easyPasswordList = api_get_easy_password_list();
-        $easyPasswordList = array_map('api_get_encrypted_password', $easyPasswordList);
-        $easyPasswordList = array_map(array('Database', 'escape_string'), $easyPasswordList);
-        $easyPassword = implode("' OR password LIKE '", $easyPasswordList);
-
-        $sql .= "AND password LIKE '$easyPassword' ";
-    }*/
-
     if (!in_array($direction, array('ASC','DESC'))) {
     	$direction = 'ASC';
     }

+ 2 - 0
main/badge/assign.php

@@ -115,6 +115,8 @@ $form->addRule('skill', get_lang('ThisFieldIsRequired'), 'required');
 $form->addSelect('acquired_level', get_lang('AcquiredLevel'), $acquiredLevel);
 $form->addRule('acquired_level', get_lang('ThisFieldIsRequired'), 'required');
 $form->addTextarea('argumentation', get_lang('Argumentation'), ['rows' => 6]);
+$form->addRule('argumentation', get_lang('ThisFieldIsRequired'), 'required');
+$form->addRule('argumentation', sprintf(get_lang('ThisTextShouldBeAtLeastXCharsLong'), 10), 'mintext', 10);
 $form->applyFilter('argumentation', 'trim');
 $form->addButtonSave(get_lang('Save'));
 $form->setDefaults($formDefaultValues);

+ 10 - 3
main/exercise/exercise.class.php

@@ -200,8 +200,6 @@ class Exercise
             $this->expired_time = $object->expired_time;
 
             //Checking if question_order is correctly set
-
-            //$this->questionList     = $this->selectQuestionList(true);
             if ($parseQuestionList) {
                 $this->setQuestionList();
             }
@@ -5325,7 +5323,16 @@ class Exercise
         $subject = get_lang('ExerciseAttempted');
 
         if (!empty($sessionId)) {
-            $teachers = CourseManager::get_coach_list_from_course_code($courseCode, $sessionId);
+            $addGeneralCoach = true;
+            $setting = api_get_configuration_value('block_quiz_mail_notification_general_coach');
+            if ($setting === true) {
+                $addGeneralCoach = false;
+            }
+            $teachers = CourseManager::get_coach_list_from_course_code(
+                $courseCode,
+                $sessionId,
+                $addGeneralCoach
+            );
         } else {
             $teachers = CourseManager::get_teacher_list_from_course_code($courseCode);
         }

+ 0 - 1
main/exercise/exercise_result.php

@@ -36,7 +36,6 @@ if (empty($origin)) {
 
 /** @var Exercise $objExercise */
 if (empty($objExercise)) {
-
     $objExercise = Session::read('objExercise');
 }
 if (empty($remind_list)) {

+ 2 - 1
main/exercise/overview.php

@@ -33,6 +33,7 @@ if (!$result) {
 $gradebook = isset($_GET['gradebook']) ? Security :: remove_XSS($_GET['gradebook']) : null;
 $learnpath_id = isset($_REQUEST['learnpath_id']) ? intval($_REQUEST['learnpath_id']) : null;
 $learnpath_item_id = isset($_REQUEST['learnpath_item_id']) ? intval($_REQUEST['learnpath_item_id']) : null;
+$learnpathItemViewId = isset($_REQUEST['learnpath_item_view_id']) ? intval($_REQUEST['learnpath_item_view_id']) : null;
 $origin = api_get_origin();
 
 $interbreadcrumb[] = array("url" => "exercise.php?gradebook=$gradebook", "name" => get_lang('Exercises'));
@@ -115,7 +116,7 @@ if (isset($exercise_stat_info['exe_id'])) {
 
 // 2. Exercise button
 // Notice we not add there the lp_item_view_id because is not already generated
-$exercise_url = api_get_path(WEB_CODE_PATH) . 'exercise/exercise_submit.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id.'&origin='.$origin.'&learnpath_id='.$learnpath_id.'&learnpath_item_id='.$learnpath_item_id.$extra_params;
+$exercise_url = api_get_path(WEB_CODE_PATH) . 'exercise/exercise_submit.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id.'&origin='.$origin.'&learnpath_id='.$learnpath_id.'&learnpath_item_id='.$learnpath_item_id.'&learnpath_item_view_id='.$learnpathItemViewId.$extra_params;
 $exercise_url_button = Display::url(
     $label,
     $exercise_url,

+ 15 - 9
main/forum/forumfunction.inc.php

@@ -139,7 +139,7 @@ function handle_forum_and_forumcategories($lp_id = null)
             $link_info = GradebookUtils::isResourceInCourseGradebook(
                 api_get_course_id(),
                 5,
-                intval($list_threads[$i]['thread_id']),
+                $list_threads[$i]['thread_id'],
                 api_get_session_id()
             );
             if ($link_info !== false) {
@@ -841,7 +841,7 @@ function deleteForumCategoryThread($content, $id)
         $return_message = get_lang('ForumCategoryDeleted');
 
         if (!empty($forum_list)) {
-            $sql = "SELECT forum_id FROM ".$table_forums."
+            $sql = "SELECT forum_id FROM $table_forums
                     WHERE c_id = $course_id AND forum_category='".$id."'";
             $result = Database::query($sql);
             $row = Database::fetch_array($result);
@@ -863,13 +863,19 @@ function deleteForumCategoryThread($content, $id)
         $return_message = get_lang('ForumDeleted');
 
         if (!empty($number_threads)) {
-            $sql = "SELECT thread_id FROM".$table_forum_thread."
-                    WHERE c_id = $course_id AND forum_id='".$id."'";
+            $sql = "SELECT thread_id FROM $table_forum_thread
+                    WHERE c_id = $course_id AND forum_id = $id ";
             $result = Database::query($sql);
             $row = Database::fetch_array($result);
             foreach ($row as $arr_forum) {
                 $forum_id = $arr_forum['thread_id'];
-                api_item_property_update($_course, 'forum_thread', $forum_id, 'delete', api_get_user_id());
+                api_item_property_update(
+                    $_course,
+                    'forum_thread',
+                    $forum_id,
+                    'delete',
+                    api_get_user_id()
+                );
             }
         }
     }
@@ -1464,9 +1470,9 @@ function get_forums(
 ) {
     $course_info = api_get_course_info($course_code);
 
-    $table_forums = Database :: get_course_table(TABLE_FORUM);
-    $table_threads = Database :: get_course_table(TABLE_FORUM_THREAD);
-    $table_item_property = Database :: get_course_table(TABLE_ITEM_PROPERTY);
+    $table_forums = Database::get_course_table(TABLE_FORUM);
+    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
+    $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
 
     // Condition for the session
     $session_id = intval($sessionId) ?: api_get_session_id();
@@ -1522,7 +1528,6 @@ function get_forums(
                     item_properties.c_id = $course_id
                 GROUP BY threads.forum_id";
 
-
         // Course Admin
         if (api_is_allowed_to_edit()) {
             // Select all the forum information of all forums (that are not deleted).
@@ -1616,6 +1621,7 @@ function get_forums(
                     api_is_allowed_to_edit(),
                     $course_id
                 );
+
                 if ($last_post_info_of_forum) {
                     $forum_list[$key]['last_post_id'] = $last_post_info_of_forum['last_post_id'];
                     $forum_list[$key]['last_poster_id'] = $last_post_info_of_forum['last_poster_id'];

+ 8 - 21
main/forum/viewforumcategory.php

@@ -243,28 +243,12 @@ if ($action_forums != 'add') {
     );
 
 
-    if ($descriptionCategory != '' && trim($descriptionCategory)!= '&nbsp;')
-    {
+    if ($descriptionCategory != '' && trim($descriptionCategory)!= '&nbsp;') {
         $html .= '<div class="forum-description">'.$descriptionCategory.'</div>';
     }
-    /* echo '<tr><th class="forum_head" '.(api_is_allowed_to_edit(null, true) ? 'colspan="5"' : 'colspan="6"').'>'; */
-
-
-    // Step 3: The interim headers (for the forum).
-    /*
-    echo '<tr class="forum_header">';
-    echo '<td colspan="2">'.get_lang('Forum').'</td>';
-    echo '<td>'.get_lang('ForumThreads').'</td>';
-    echo '<td>'.get_lang('Posts').'</td>';
-    echo '<td>'.get_lang('LastPosts').'</td>';
-    echo '<td>'.get_lang('Actions').'</td>';
-    echo '</tr>';
-    echo '</thead>';
-    */
 
     $html .= '</div>';
     echo $html;
-
     echo '<div class="forum_display">';
     // The forums in this category.
     $forums_in_category = get_forums_in_category($forum_category['cat_id']);
@@ -431,13 +415,16 @@ if ($action_forums != 'add') {
                 $html .= '<div class="col-md-2">';
                 $html .= $newPost . '</div>';
 
+                $poster_id = 0;
+                $name = '';
                 // the last post in the forum
-                if ($forum['last_poster_name'] != '') {
+                if (isset($forum['last_poster_name']) && $forum['last_poster_name'] != '') {
                     $name = $forum['last_poster_name'];
-                    $poster_id = 0;
                 } else {
-                    $name = api_get_person_name($forum['last_poster_firstname'], $forum['last_poster_lastname']);
-                    $poster_id = $forum['last_poster_id'];
+                    if (isset($forum['last_poster_lastname'])) {
+                        $name = api_get_person_name($forum['last_poster_firstname'], $forum['last_poster_lastname']);
+                        $poster_id = $forum['last_poster_id'];
+                    }
                 }
                 $html .= '<div class="col-md-6">';
                 if (!empty($forum['last_post_id'])) {

+ 13 - 3
main/gradebook/index.php

@@ -756,6 +756,7 @@ $no_qualification = false;
 // Show certificate link.
 $certificate = array();
 $actionsLeft = '';
+$hideCertificateExport = api_get_setting('hide_certificate_export_link');
 
 if (!empty($selectCat)) {
     $cat = new Category();
@@ -769,7 +770,8 @@ if (!empty($selectCat)) {
                 $selectCat,
                 $stud_id
             );
-            if (isset($certificate['pdf_url'])) {
+
+            if (isset($certificate['pdf_url']) && $hideCertificateExport !== 'true') {
                 $actionsLeft .= Display::url(Display::returnFontAwesomeIcon('file-pdf-o') .
                     get_lang('DownloadCertificatePdf'),
                     $certificate['pdf_url'],
@@ -777,7 +779,13 @@ if (!empty($selectCat)) {
                 );
             }
 
-            $currentScore = Category::getCurrentScore($stud_id, $selectCat, $course_code, $session_id, true);
+            $currentScore = Category::getCurrentScore(
+                $stud_id,
+                $selectCat,
+                $course_code,
+                $session_id,
+                true
+            );
             Category::registerCurrentScore($currentScore, $stud_id, $selectCat);
         }
     }
@@ -785,7 +793,9 @@ if (!empty($selectCat)) {
 
 if (!api_is_allowed_to_edit(null, true)) {
     $actionsLeft .= Display::url(
-        Display::returnFontAwesomeIcon('file-pdf-o') . get_lang('DownloadReportPdf'),
+        Display::returnFontAwesomeIcon('file-pdf-o').get_lang(
+            'DownloadReportPdf'
+        ),
         api_get_self()."?".api_get_self()."&action=export_table",
         ['class' => 'btn btn-default']
     );

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

@@ -33,7 +33,6 @@ switch ($_REQUEST['action']) {
         $json = [
             'status' => true,
             'data' => [
-                'chatIsDenied' => $courseChatUtils->isChatDenied(),
                 'oldFileSize' => file_exists($filePath) ? filesize($filePath) : 0,
                 'history' => $newFileSize !== $oldFileSize ? $courseChatUtils->readMessages(false, $friend) : null,
                 'usersOnline' => $newUsersOnline,

+ 0 - 2
main/inc/ajax/online.ajax.php

@@ -6,9 +6,7 @@ $action = $_GET['a'];
 
 switch ($action) {
     case 'load_online_user':
-
         $images_to_show = MAX_ONLINE_USERS;
-
         $page = intval($_REQUEST['online_page_nr']);
         $max_page = ceil(who_is_online_count()/$images_to_show);
 

+ 3 - 1
main/inc/ajax/social.ajax.php

@@ -45,7 +45,9 @@ switch ($action) {
         }
         if (isset($_GET['denied_friend_id'])) {
             SocialManager::invitation_denied($_GET['denied_friend_id'], $current_user_id);
-            Display::display_confirmation_message(api_xml_http_response_encode(get_lang('InvitationDenied')));
+            Display::addFlash(
+                Display::return_message(get_lang('InvitationDenied'), 'success')
+            );
 
             header('Location: ' . api_get_path(WEB_CODE_PATH) . 'social/invitations.php');
         }

+ 0 - 37
main/inc/lib/CourseChatUtils.php

@@ -331,43 +331,6 @@ class CourseChatUtils
         $em->flush();
     }
 
-    /**
-     * Check if the connection is denied for chat
-     * @return bool
-     * @throws \Doctrine\ORM\ORMException
-     * @throws \Doctrine\ORM\OptimisticLockException
-     * @throws \Doctrine\ORM\TransactionRequiredException
-     */
-    public function isChatDenied()
-    {
-        if (ChamiloSession::read('origin', null) !== 'whoisonline') {
-            return false;
-        }
-
-        $talkTo = ChamiloSession::read('target', 0);
-
-        if (!$talkTo) {
-            return true;
-        }
-
-        $em = Database::getManager();
-        $user = $em->find('ChamiloUserBundle:User', $talkTo);
-
-        if ($user->getChatcallText() === 'DENIED') {
-            $user
-                ->setChatcallDate(null)
-                ->setChatcallUserId(null)
-                ->setChatcallText(null);
-
-            $em->merge($user);
-            $em->flush();
-
-            return true;
-        }
-
-        return false;
-    }
-
     /**
      * Get the emoji allowed on course chat
      * @return array

+ 14 - 15
main/inc/lib/TicketManager.php

@@ -82,7 +82,7 @@ class TicketManager
             $direction = 'ASC';
         }
         $column = intval($column);
-        $from 	= intval($from);
+        $from = intval($from);
         $numberItems = intval($numberItems);
 
         //$sql .= " ORDER BY col$column $direction ";
@@ -181,7 +181,7 @@ class TicketManager
             if (self::userIsAssignedToCategory($userId, $categoryId) == false) {
                 $params = [
                     'category_id' => $categoryId,
-                    'user_id' => $userId,
+                    'user_id' => $userId
                 ];
                 Database::insert($table, $params);
             }
@@ -328,7 +328,6 @@ class TicketManager
         if (!empty($assigned_user)) {
             $assignedUserInfo = api_get_user_info($assigned_user);
             if (empty($assignedUserInfo)) {
-
                 return false;
             }
         }
@@ -545,7 +544,6 @@ class TicketManager
 
             return true;
         } else {
-
             return false;
         }
     }
@@ -562,7 +560,6 @@ class TicketManager
         $user_id = intval($user_id);
 
         if (empty($ticket_id)) {
-
             return false;
         }
 
@@ -691,7 +688,7 @@ class TicketManager
             'sys_insert_datetime' => $now,
             'sys_lastedit_user_id' => $user_id,
             'sys_lastedit_datetime' => $now,
-            'status' => $status,
+            'status' => $status
         ];
         Database::insert($table_support_messages, $params);
 
@@ -1271,7 +1268,7 @@ class TicketManager
                     status.name as status, 
                     priority.name priority
                 FROM $table_support_tickets ticket,
-                    $table_support_category cat ,
+                    $table_support_category cat,
                     $table_support_priority priority,
                     $table_support_status status
 		        WHERE
@@ -1605,7 +1602,7 @@ class TicketManager
         $table_support_tickets = Database::get_main_table(TABLE_TICKET_TICKET);
         $now = api_get_utc_datetime();
         $userId = api_get_user_id();
-        $sql = "UPDATE $table_support_tickets  *
+        $sql = "UPDATE $table_support_tickets
                 SET
                     status_id = '".self::STATUS_CLOSE."',
                     sys_lastedit_user_id ='$userId',
@@ -1690,11 +1687,15 @@ class TicketManager
             $user_id = api_get_user_id();
         }
 
-        $sql = "SELECT ticket.code, ticket.sys_insert_datetime ,
-                ticket.sys_lastedit_datetime , cat.name as category ,
-                CONCAT(user.lastname,' ', user.firstname) AS fullname ,
-                status.name as status , ticket.total_messages as messages ,
-                ticket.assigned_last_user as responsable
+        $sql = "SELECT 
+                    ticket.code, 
+                    ticket.sys_insert_datetime,
+                    ticket.sys_lastedit_datetime, 
+                    cat.name as category,
+                    CONCAT(user.lastname,' ', user.firstname) AS fullname,
+                    status.name as status, 
+                    ticket.total_messages as messages,
+                    ticket.assigned_last_user as responsable
                 FROM $table_support_tickets ticket,
                 $table_support_category cat ,
                 $table_support_priority priority,
@@ -2164,8 +2165,6 @@ class TicketManager
         return $form;
     }
 
-    //
-
     /**
      * @return array
      */

+ 40 - 350
main/inc/lib/api.lib.php

@@ -352,7 +352,6 @@ define('USER_RELATION_TYPE_BOSS', 8);
 
 // Gradebook link constants
 // Please do not change existing values, they are used in the database !
-
 define('GRADEBOOK_ITEM_LIMIT', 1000);
 
 define('LINK_EXERCISE', 1);
@@ -434,7 +433,6 @@ define('GROUP_TITLE_LENGTH', 50);
 
 // Exercise
 // @todo move into a class
-
 define('ALL_ON_ONE_PAGE', 1);
 define('ONE_PER_PAGE', 2);
 
@@ -607,11 +605,10 @@ define('RESOURCE_ATTENDANCE', 'attendance');
 define('RESOURCE_WORK', 'work');
 define('RESOURCE_SESSION_COURSE', 'session_course');
 define('RESOURCE_GRADEBOOK', 'gradebook');
-
 define('ADD_THEMATIC_PLAN', 6);
 
 // Make sure the CHAMILO_LOAD_WYSIWYG constant is defined
-define('MAX_ONLINE_USERS', 8);
+define('MAX_ONLINE_USERS', 12);
 
 // Make sure the CHAMILO_LOAD_WYSIWYG constant is defined
 // To remove CKeditor libs from HTML, set this constant to true before loading
@@ -711,7 +708,6 @@ function api_get_path($path = '', $configuration = [])
     }
 
     $paths = [];
-
     // Initialise cache with default values.
     if (!array_key_exists($root_web, $paths)) {
         $paths[$root_web] = array(
@@ -892,7 +888,6 @@ function api_get_path($path = '', $configuration = [])
     return $path;
 }
 
-
 /**
  * Gets a modified version of the path for the CDN, if defined in
  * configuration.php
@@ -1036,7 +1031,6 @@ function api_valid_email($address)
     return filter_var($address, FILTER_VALIDATE_EMAIL);
 }
 
-
 /* PROTECTION FUNCTIONS
    Use these functions to protect your scripts. */
 
@@ -2288,6 +2282,7 @@ function api_get_session_condition(
  * if (api_get_setting('show_navigation_menu') == true) //INCORRECT
  * @param string    $variable The variable name
  * @param string    $key The subkey (sub-variable) if any. Defaults to NULL
+ * @return string
  * @author René Haentjens
  * @author Bart Mollet
  */
@@ -2376,23 +2371,24 @@ function api_get_self() {
 
 /**
  * Checks whether current user is a platform administrator
- * @param boolean Whether session admins should be considered admins or not
- * @param boolean Whether HR directors should be considered admins or not
+ * @param boolean $allowSessionAdmins Whether session admins should be considered admins or not
+ * @param boolean $allowDrh Whether HR directors should be considered admins or not
  * @return boolean True if the user has platform admin rights,
  * false otherwise.
  * @see usermanager::is_admin(user_id) for a user-id specific function
  */
-function api_is_platform_admin($allow_sessions_admins = false, $allow_drh = false)
+function api_is_platform_admin($allowSessionAdmins = false, $allowDrh = false)
 {
-    if (isset($_SESSION['is_platformAdmin']) && $_SESSION['is_platformAdmin']) {
+    $isAdmin = Session::read('is_platformAdmin');
+    if ($isAdmin) {
         return true;
     }
-    $_user = api_get_user_info();
+    $user = api_get_user_info();
     return
-        isset($_user['status']) &&
+        isset($user['status']) &&
         (
-            ($allow_sessions_admins && $_user['status'] == SESSIONADMIN) ||
-            ($allow_drh && $_user['status'] == DRH)
+            ($allowSessionAdmins && $user['status'] == SESSIONADMIN) ||
+            ($allowDrh && $user['status'] == DRH)
         );
 }
 
@@ -2489,95 +2485,6 @@ function api_is_course_tutor() {
     return Session::read('is_courseTutor');
 }
 
-/**
- * @param int $user_id
- *
- * @return array
- */
-function api_get_user_platform_status($user_id = null) {
-    $status     = array();
-    $user_id    = intval($user_id);
-    if (empty($user_id)) {
-        $user_id = api_get_user_id();
-    }
-
-    if (empty($user_id)) {
-        return false;
-    }
-    $group_id   = api_get_group_id();
-    $course_id  = api_get_course_int_id();
-    $course_code= api_get_course_id();
-    $session_id = api_get_session_id();
-
-    //Group (in course)
-    if ($group_id && $course_id) {
-        $group_status = array();
-        $groupInfo = GroupManager::get_group_properties($group_id);
-        $is_subscribed = GroupManager::is_subscribed($user_id, $groupInfo['iid']);
-        if ($is_subscribed) {
-            $group_status = array('id'=> $group_id , 'status' => 'student');
-            $is_tutor = GroupManager::is_tutor_of_group($user_id, $groupInfo['iid']);
-            if ($is_tutor) {
-                $group_status['status'] = 'tutor';
-            } else {
-                $group_status['status'] = 'student';
-            }
-        }
-        $status['group'] = $group_status;
-    }
-
-    //Session
-    if ($session_id && $course_id) {
-        $session_status = array('id' => $session_id, 'course_id' => $course_id);
-        $session_user_status = SessionManager::get_user_status_in_course_session(
-            $user_id,
-            $course_id,
-            $session_id
-        );
-
-        switch ($session_user_status) {
-            case 0:
-                $session_status['status'] = 'student';
-               break;
-            case 2:
-                $session_status['status'] = 'coach';
-            break;
-        }
-        $is_general_coach = SessionManager::user_is_general_coach($user_id, $session_id);
-        if ($is_general_coach) {
-            $session_status['status'] = 'general_coach';
-        }
-        $status['session'] = $session_status;
-
-    } elseif($course_id) {
-        //Course
-        $course_status = array();
-        if ($course_id) {
-            $user_course_status = CourseManager::get_user_in_course_status($user_id, $course_code);
-
-            if ($user_course_status) {
-                $course_status = array('id'=> $course_id);
-                switch($user_course_status) {
-                    case 1:
-                        $course_status['status'] = 'teacher';
-                        break;
-                    case 5:
-                        $course_status['status'] = 'student';
-                        //check if tutor
-                        $tutor_course_status = CourseManager::get_tutor_in_course_status($user_id, $course_id);
-                        if ($tutor_course_status) {
-                            $course_status['status'] = 'tutor';
-                        }
-                        break;
-                }
-            }
-        }
-        $status['course'] = $course_status;
-    }
-
-    return $status;
-}
-
 /**
  * @param int $user_id
  * @param int $courseId
@@ -2698,8 +2605,8 @@ function api_is_student()
 {
     $user = api_get_user_info();
     return isset($user['status']) && $user['status'] == STUDENT;
-
 }
+
 /**
  * Checks whether the current user has the status 'teacher'
  * @return boolean True if current user is a human resources manager
@@ -2752,9 +2659,6 @@ function api_is_session_in_category($session_id, $category_name)
     }
 }
 
-/* DISPLAY OPTIONS
-   student view, title, message boxes,... */
-
 /**
  * Displays the title of a tool.
  * Normal use: parameter is a string:
@@ -2892,8 +2796,12 @@ function api_display_tool_view_option() {
  * @return boolean true: the user has the rights to edit, false: he does not
  */
 
-function api_is_allowed_to_edit($tutor = false, $coach = false, $session_coach = false, $check_student_view = true)
-{
+function api_is_allowed_to_edit(
+    $tutor = false,
+    $coach = false,
+    $session_coach = false,
+    $check_student_view = true
+) {
     $my_session_id = api_get_session_id();
     $is_allowed_coach_to_edit = api_is_coach(null, null, $check_student_view);
     $session_visibility = api_get_session_visibility($my_session_id);
@@ -3373,8 +3281,6 @@ function api_not_allowed($print_headers = false, $message = null)
         if ($casEnabled) {
             $msg .= "</div>";
         }
-
-        //$msg .= '<hr/><p style="text-align:center"><a href="'.$home_url.'">'.get_lang('ReturnToCourseHomepage').'</a></p>';
     } else {
         // we were not in a course, return to home page
         $msg = Display::return_message(
@@ -3389,11 +3295,6 @@ function api_not_allowed($print_headers = false, $message = null)
     exit;
 }
 
-
-/* WHAT'S NEW
-   functions for the what's new icons
-   in the user course list */
-
 /**
  * Gets a UNIX timestamp from a database (MySQL) datetime format string
  * @param $last_post_datetime standard output date in a sql query
@@ -3409,16 +3310,6 @@ function convert_sql_date($last_post_datetime) {
     return mktime((int)$hour, (int)$min, (int)$sec, (int)$month, (int)$day, (int)$year);
 }
 
-/**
- * Gets a database (MySQL) datetime format string from a UNIX timestamp
- * @param   int     UNIX timestamp, as generated by the time() function. Will be generated if parameter not provided
- * @return  string  MySQL datetime format, like '2009-01-30 12:23:34'
- */
-function api_get_datetime($time = null) {
-    if (!isset($time)) { $time = time(); }
-    return date('Y-m-d H:i:s', $time);
-}
-
 /**
  * Gets item visibility from the item_property table
  *
@@ -4152,9 +4043,7 @@ function api_display_language_form($hide_if_no_choice = false)
 
     $original_languages = $language_list['name'];
     $folder = $language_list['folder']; // This line is probably no longer needed.
-    $html = '
-    <script type="text/javascript">
-    <!--
+    $html = '<script>    
     $(document).ready(function() {
         $("#language_list").change(function() {
             jumpMenu("parent",this,0);
@@ -4165,7 +4054,6 @@ function api_display_language_form($hide_if_no_choice = false)
         eval(targ+".location=\'"+selObj.options[selObj.selectedIndex].value+"\'");
         if (restore) selObj.selectedIndex=0;
     }
-    //-->
     </script>';
     $html .= '<form id="lang_form" name="lang_form" method="post" action="'.api_get_self().'">';
     $html .= '<label style="display: none;" for="language_list">' . get_lang('Language') . '</label>';
@@ -4195,7 +4083,8 @@ function api_display_language_form($hide_if_no_choice = false)
  */
 function api_get_languages() {
     $tbl_language = Database::get_main_table(TABLE_MAIN_LANGUAGE);
-    $sql = "SELECT * FROM $tbl_language WHERE available='1' ORDER BY original_name ASC";
+    $sql = "SELECT * FROM $tbl_language WHERE available='1' 
+            ORDER BY original_name ASC";
     $result = Database::query($sql);
     $language_list = array();
     while ($row = Database::fetch_array($result)) {
@@ -4530,6 +4419,7 @@ function api_get_permissions_for_new_files() {
     if (!isset($permissions)) {
         $permissions = trim(api_get_setting('permissions_for_new_files'));
         // The default value 0666 is according to that in the platform administration panel after fresh system installation.
+        // administration panel after fresh system installation.
         $permissions = octdec(!empty($permissions) ? $permissions : '0666');
     }
     return $permissions;
@@ -4832,7 +4722,7 @@ function api_chmod_R($path, $filemode) {
  * @return
  *   The info array.
  */
-function parse_info_file($filename) {
+function api_parse_info_file($filename) {
     $info = array();
 
     if (!file_exists($filename)) {
@@ -4893,7 +4783,6 @@ function parse_info_file($filename) {
     return $info;
 }
 
-
 /**
  * Gets Chamilo version from the configuration files
  * @return string   A string of type "1.8.4", or an empty string if the version could not be found
@@ -5225,34 +5114,6 @@ function api_get_access_url($id, $returnDefault = true)
     return $result;
 }
 
-/**
- * Adds an access URL into the database
- * @param string    URL
- * @param string    Description
- * @param int       Active (1= active, 0=disabled)
- * @return int      The new database id, or the existing database id if this url already exists
- */
-function api_add_access_url($u, $d = '', $a = 1) {
-    $t_au = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
-    $u = Database::escape_string($u);
-    $d = Database::escape_string($d);
-    $a = (int) $a;
-    $sql = "SELECT id FROM $t_au WHERE url LIKE '$u'";
-    $res = Database::query($sql);
-    if ($res === false) {
-        // Problem querying the database - return false.
-        return false;
-    }
-    if (Database::num_rows($res) > 0) {
-        return Database::result($res, 0, 'id');
-    }
-    $ui = api_get_user_id();
-
-    $sql = "INSERT INTO $t_au (url,description,active,created_by,tms) VALUES ('$u','$d',$a,$ui,'')";
-    $res = Database::query($sql);
-    return ($res === false) ? false : Database::insert_id();
-}
-
 /**
  * Gets all the current settings for a specific access url
  * @param string    The category, if any, that we want to get
@@ -5290,65 +5151,6 @@ function & api_get_settings($cat = null, $ordering = 'list', $access_url = 1, $u
     return $result;
 }
 
-/**
- * Gets the distinct settings categories
- * @param array     Array of strings giving the categories we want to excluded
- * @param int       Access URL. Optional. Defaults to 1
- * @return array    A list of categories
- */
-function & api_get_settings_categories($exceptions = array(), $access_url = 1) {
-    $access_url = (int) $access_url;
-    $t_cs = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
-    $list = "'".implode("','",$exceptions)."'";
-    $sql = "SELECT DISTINCT category FROM $t_cs WHERE category is NOT NULL ";
-    if ($list != "'',''" && $list != "''" && !empty($list)) {
-        $sql .= " AND category NOT IN ($list) ";
-    }
-    $result = Database::store_result(Database::query($sql));
-    return $result;
-}
-
-/**
- * Deletes a setting
- * @param string    Variable
- * @param string    Subkey
- * @param int       Access URL
- * @return boolean  False on failure, true on success
- */
-
-function api_delete_setting($v, $s = null, $a = 1) {
-    if (empty($v)) { return false; }
-    $t_cs = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
-    $v = Database::escape_string($v);
-    $a = (int) $a;
-    if (empty($a)) { $a = 1; }
-    if (!empty($s)) {
-        $s = Database::escape_string($s);
-        $sql = "DELETE FROM $t_cs WHERE variable = '$v' AND subkey = '$s' AND access_url = $a";
-        $r = Database::query($sql);
-        return $r;
-    }
-    $sql = "DELETE FROM $t_cs WHERE variable = '$v' AND access_url = $a";
-    $r = Database::query($sql);
-    return $r;
-}
-
-/**
- * Deletes all the settings from one category
- * @param string    Subkey
- * @param int       Access URL
- * @return boolean  False on failure, true on success
- */
-function api_delete_category_settings_by_subkey($subkey, $access_url_id = 1) {
-    if (empty($subkey)) { return false; }
-    $t_cs = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
-    $subkey = Database::escape_string($subkey);
-    $access_url_id = intval($access_url_id);
-    $sql = "DELETE FROM $t_cs WHERE subkey = '$subkey' AND access_url = $access_url_id";
-    $r = Database::query($sql);
-    return $r;
-}
-
 /**
  * Sets a platform configuration setting to a given value
  * @param string    The value we want to record
@@ -5781,23 +5583,6 @@ function api_get_status_of_user_in_course($user_id, $courseId)
     }
 }
 
-/**
- * Checks whether the curent user is in a course or not.
- *
- * @param string        The course code - optional (takes it from session if not given)
- * @return boolean
- * @author Yannick Warnier <yannick.warnier@beeznest.com>
- */
-function api_is_in_course($course_code = null) {
-    if (isset($_SESSION['_course']['sysCode'])) {
-        if (!empty($course_code)) {
-            return $course_code == $_SESSION['_course']['sysCode'];
-        }
-        return true;
-    }
-    return false;
-}
-
 /**
  * Checks whether the curent user is in a group or not.
  *
@@ -5832,7 +5617,6 @@ function api_is_in_group($groupIdParam = null, $courseCodeParam = null)
     return false;
 }
 
-
 /**
  * Checks whether a secret key is valid
  * @param string $original_key_secret  - secret key from (webservice) client
@@ -6064,18 +5848,6 @@ function api_get_tool_information_by_name($name) {
     return Database::fetch_array($rs, 'ASSOC');
 }
 
-/**
- * @deprecated use api_trunc_str() instead.
- */
-function shorten($input, $length = 15, $encoding = null) {
-    $length = intval($length);
-    if (!$length) {
-        $length = 15;
-    }
-    return api_trunc_str($input, $length, '...', false, $encoding);
-}
-
-
 /**
  * Function used to protect a "global" admin script.
  * The function blocks access when the user has no global platform admin rights.
@@ -6206,8 +5978,7 @@ function api_get_template($path_type = 'rel') {
  * @return bool or return text array if $format=check_browser
  * @author Juan Carlos Raña Trabado
  */
-
-function api_browser_support($format = "")
+function api_browser_support($format = '')
 {
     $browser = new Browser();
     $current_browser = $browser->getBrowser();
@@ -6457,7 +6228,6 @@ function api_get_jqgrid_js() {
     return api_get_jquery_libraries_js(array('jqgrid'));
 }
 
-
 /**
  * Returns the jquery library js and css headers
  *
@@ -7010,36 +6780,6 @@ function api_detect_user_roles($user_id, $courseId, $session_id = 0)
     return $user_roles;
 }
 
-function api_get_roles_to_string($roles) {
-    $role_names = array();
-    if (!empty($roles)) {
-        foreach ($roles as $role) {
-            $role_names[] = get_status_from_code($role);
-        }
-    }
-    if (!empty($role_names)) {
-        return implode(', ', $role_names);
-    }
-    return null;
-}
-
-function role_actions() {
-    return array(
-        'course' => array(
-            'create',
-            'read',
-            'edit',
-            'delete'
-        ),
-        'admin' => array(
-            'create',
-            'read',
-            'edit',
-            'delete'
-        )
-    );
-}
-
 /**
  * @param int $courseId
  * @param int $session_id
@@ -7161,12 +6901,6 @@ function api_set_settings_and_plugins() {
     $_SESSION['_plugins'] = $_plugins;
 }
 
-function api_set_setting_last_update()
-{
-    // Saving latest refresh.
-    api_set_setting('settings_latest_update', api_get_utc_datetime());
-}
-
 /**
  * Tries to set memory limit, if authorized and new limit is higher than current
  * @param string New memory limit
@@ -7228,16 +6962,17 @@ function api_get_bytes_memory_limit($mem){
 /**
  * Finds all the information about a user from username instead of user id
  *
+ * @param string $officialCode
  * @return array $user_info user_id, lastname, firstname, username, email, ...
  * @author Yannick Warnier <yannick.warnier@beeznest.com>
  */
-function api_get_user_info_from_official_code($official_code = '')
+function api_get_user_info_from_official_code($officialCode)
 {
-    if (empty($official_code)) {
+    if (empty($officialCode)) {
         return false;
     }
     $sql = "SELECT * FROM ".Database :: get_main_table(TABLE_MAIN_USER)."
-            WHERE official_code ='".Database::escape_string($official_code)."'";
+            WHERE official_code ='".Database::escape_string($officialCode)."'";
     $result = Database::query($sql);
     if (Database::num_rows($result) > 0) {
         $result_array = Database::fetch_array($result);
@@ -7355,20 +7090,6 @@ function api_get_password_checker_js($usernameInputId, $passwordInputId)
     return $js;
 }
 
-/**
- * Gets an array with "easy" passwords
- * @return array
- */
-function api_get_easy_password_list()
-{
-    $passwordList = array('123', '1234', '123456', 'admin', 'user', 'student', 'teacher');
-    $file = api_get_path(CONFIGURATION_PATH).'easy_password_list.php';
-    if (file_exists($file)) {
-        $passwordList = require_once $file;
-    }
-    return $passwordList;
-}
-
 /**
  * @param string $username
  * create an user extra field called 'captcha_blocked_until_date'
@@ -7466,9 +7187,9 @@ function api_remove_tags_with_space($in_html, $in_double_quote_replace = true) {
  */
 function api_drh_can_access_all_session_content()
 {
-    $a = api_get_setting('drh_can_access_all_session_content');
+    $value = api_get_setting('drh_can_access_all_session_content');
 
-    return $a === 'true';
+    return $value === 'true';
 }
 
 /**
@@ -7486,8 +7207,8 @@ function api_get_default_tool_setting($tool, $setting, $defaultValue)
     ) {
         return $_configuration[$tool]['default_settings'][$setting];
     }
-    return $defaultValue;
 
+    return $defaultValue;
 }
 
 /**
@@ -7557,30 +7278,6 @@ function api_is_allowed_in_course()
     return Session::read('is_allowed_in_course');
 }
 
-/**
- * Show a string in
- * @param string $string Some string to dump, removing tabs, spaces,
- * newlines, etc (usually most useful for SQL queries)
- * @param int $dump Set to 1 to use print_r()
- */
-function api_error_log($string, $dump = 0)
-{
-    // Clean query
-    $bt = debug_backtrace();
-    $caller = array_shift($bt);;
-    if ($dump == 1) {
-        $string = print_r($string, 1);
-    } else {
-        $string = str_replace(array("\r", "\n", "\t", "\10"), '', $string);
-        $string = str_replace('    ',' ', $string);
-    }
-
-    error_log("-------------------------------------");
-    error_log($string);
-    error_log("File: ".$caller['file']." +".$caller['line']);
-    error_log("-------------------------------------");
-}
-
 /**
  * Set the cookie to go directly to the course code $in_firstpage
  * after login
@@ -7588,7 +7285,7 @@ function api_error_log($string, $dump = 0)
  */
 function api_set_firstpage_parameter($in_firstpage)
 {
-    setcookie("GotoCourse", $in_firstpage);
+    setcookie('GotoCourse', $in_firstpage);
 }
 
 /**
@@ -7597,7 +7294,7 @@ function api_set_firstpage_parameter($in_firstpage)
  */
 function api_delete_firstpage_parameter()
 {
-    setcookie("GotoCourse", "", time() - 3600);
+    setcookie('GotoCourse', '', time() - 3600);
 }
 
 /**
@@ -7643,7 +7340,8 @@ function api_get_protocol()
  * @param string $in_text
  * @return string
  */
-function convert_double_quote_to_single($in_text) {
+function convert_double_quote_to_single($in_text)
+{
     return api_preg_replace('/"/', "''", $in_text);
 }
 
@@ -7659,7 +7357,7 @@ function api_get_origin()
         return $_REQUEST['origin'] === 'learnpath' ? 'learnpath' : '';
     }
 
-    return null;
+    return '';
 }
 
 /**
@@ -7814,7 +7512,7 @@ function api_get_users_status_ignored_in_reports($format = 'array')
  */
 function api_set_site_use_cookie_warning_cookie()
 {
-    setcookie("ChamiloUsesCookies", "ok", time()+31556926);
+    setcookie('ChamiloUsesCookies', 'ok', time()+31556926);
 }
 
 /**
@@ -8027,15 +7725,6 @@ function api_mail_html(
             }
             $o++;
         }
-//    } elseif (is_array($_FILES)) {
-//        $data_file = $_FILES;
-//        $o = 0;
-//        foreach ($data_file as $file_attach) {
-//            if (!empty($file_attach['tmp_name']) && !empty($file_attach['name'])) {
-//                $mail->AddAttachment($file_attach['tmp_name'], $file_attach['name']);
-//            }
-//            $o++;
-//        }
     }
 
     // Only valid addresses are accepted.
@@ -8194,7 +7883,8 @@ function api_protect_limit_for_session_admin()
 }
 
 function api_is_student_view_active() {
-    return (isset($_SESSION['studentview']) && $_SESSION['studentview'] == "studentview");
+    $studentView = Session::read('studentview');
+    return $studentView == 'studentview';
 }
 
 /**
@@ -8252,4 +7942,4 @@ function api_remove_uploaded_file($type, $file)
 function api_student_boss_access_to_course_and_session()
 {
     return true;
-}
+}

+ 20 - 12
main/inc/lib/course.lib.php

@@ -339,7 +339,7 @@ class CourseManager
 
     /**
      * @param int $user_id
-     * @param string $course_code
+     * @param int $courseId
      *
      * @return mixed
      */
@@ -1728,9 +1728,10 @@ class CourseManager
      * Get a list of coaches of a course and a session
      * @param   string  Course code
      * @param   int     Session ID
+     * @param   bool $addGeneralCoach
      * @return  array   List of users
      */
-    public static function get_coach_list_from_course_code($course_code, $session_id)
+    public static function get_coach_list_from_course_code($course_code, $session_id, $addGeneralCoach = true)
     {
         if (empty($course_code) || empty($session_id)) {
             return array();
@@ -1755,16 +1756,18 @@ class CourseManager
             $users[$user['user_id']] = $user_info;
         }
 
-        $table = Database::get_main_table(TABLE_MAIN_SESSION);
-        // We get the session coach.
-        $sql = 'SELECT id_coach FROM ' . $table . ' WHERE id=' . $session_id;
-        $rs = Database::query($sql);
-        $session_id_coach = Database::result($rs, 0, 'id_coach');
-        $user_info = api_get_user_info($session_id_coach);
-        $user_info['status'] = $user['status'];
-        //$user_info['tutor_id'] = $user['tutor_id'];
-        $user_info['email'] = $user['email'];
-        $users[$session_id_coach] = $user_info;
+        if ($addGeneralCoach) {
+            $table = Database::get_main_table(TABLE_MAIN_SESSION);
+            // We get the session coach.
+            $sql = 'SELECT id_coach FROM '.$table.' WHERE id='.$session_id;
+            $rs = Database::query($sql);
+            $session_id_coach = Database::result($rs, 0, 'id_coach');
+            $user_info = api_get_user_info($session_id_coach);
+            $user_info['status'] = $user['status'];
+            //$user_info['tutor_id'] = $user['tutor_id'];
+            $user_info['email'] = $user['email'];
+            $users[$session_id_coach] = $user_info;
+        }
 
         return $users;
     }
@@ -2329,6 +2332,11 @@ class CourseManager
             $sql = "DELETE FROM $table_stats_uploads WHERE c_id = $courseId";
             Database::query($sql);
 
+            // Update ticket
+            $ticket = Database::get_main_table(TABLE_TICKET_TICKET);
+            $sql = "UPDATE $ticket SET course_id = NULL WHERE course_id = $courseId";
+            Database::query($sql);
+
             // Delete the course from the database
             $sql = "DELETE FROM $table_course WHERE code = '" . $codeFiltered . "'";
             Database::query($sql);

+ 2 - 2
main/inc/lib/dashboard.lib.php

@@ -45,7 +45,7 @@ class DashboardManager
         foreach ($possibleplugins as $testplugin) {
             $plugin_info_file = $dashboard_pluginpath.$testplugin."/$testplugin.info";
             if (file_exists($plugin_info_file) && is_readable($plugin_info_file)) {
-                $plugin_info = parse_info_file($plugin_info_file);
+                $plugin_info = api_parse_info_file($plugin_info_file);
 
                 // change index to lower case
                 $plugin_info = array_change_key_case($plugin_info);
@@ -216,7 +216,7 @@ class DashboardManager
                         $plugin_info_file = $dashboard_pluginpath . $testplugin . "/$testplugin.info";
                         $plugin_info = array();
                         if (file_exists($plugin_info_file)) {
-                            $plugin_info = parse_info_file($plugin_info_file);
+                            $plugin_info = api_parse_info_file($plugin_info_file);
                         }
 
                         // change keys to lower case

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

@@ -1893,7 +1893,7 @@ class DocumentManager
 
         // get extra fields
         $extraField = new ExtraField('user');
-        $extraFields = $extraField->get_all(['filter = ? AND visible = ?' => [1, 1]]);
+        $extraFields = $extraField->get_all(['filter = ? AND visible_to_self = ?' => [1, 1]]);
 
         //Student information
         $user_info = api_get_user_info($user_id);

+ 10 - 11
main/inc/lib/grade_model.lib.php

@@ -83,8 +83,7 @@ class GradeModel extends Model
         $form->addElement('header', $header);
         $id = isset($_GET['id']) ? intval($_GET['id']) : '';
         $form->addElement('hidden', 'id', $id);
-
-        $form->addElement('text', 'name', get_lang('Name'), array('size' => '70'));
+        $form->addText('name', get_lang('Name'));
         $form->addHtmlEditor(
             'description',
             get_lang('Description'),
@@ -97,14 +96,13 @@ class GradeModel extends Model
             )
         );
 
-        $form->addElement('label', get_lang('Components'));
+        $form->addLabel(get_lang('Components'), '');
 
         // Get components
         $nr_items = 2;
         $max = 10;
 
         // Setting the defaults
-
         $defaults = $this->get($id);
         if ($defaults) {
             $components = $this->get_components($defaults['id']);
@@ -112,7 +110,7 @@ class GradeModel extends Model
 
         if ($action == 'edit') {
             if (!empty($components)) {
-                $nr_items = count($components) -1;
+                $nr_items = count($components);
             }
         }
 
@@ -122,6 +120,7 @@ class GradeModel extends Model
 
         $component_array = array();
 
+
         for ($i = 0; $i <= $max; $i++) {
             $counter = $i;
             $form->addElement('text', 'components['.$i.'][percentage]', null);
@@ -130,10 +129,11 @@ class GradeModel extends Model
             $form->addElement('hidden', 'components['.$i.'][id]', null);
 
             $template_percentage =
-            '<div id=' . $i . ' style="display: '.(($i<=$nr_items)?'inline':'none').';" class="control-group">
-                <p>
-                <label class="control-label">{label}</label>
-                <div class="controls">
+            '<div id=' . $i . ' style="display: '.(($i<=$nr_items)?'inline':'none').';" class="form-group">                
+                <label for="" class="col-sm-2 control-label">
+                    {label}
+                </label>
+                <div class="col-sm-8">
                     <!-- BEGIN required --><span class="form_required">*</span> <!-- END required -->
                     {element} <!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --> % = ';
 
@@ -149,7 +149,7 @@ class GradeModel extends Model
             <a href="javascript:minItem(' . ($counter) . ')">
                 '.Display::return_icon('delete.png', get_lang('Delete'), ['id' => 'min-' . ($counter), 'style' => 'display: '.(($counter>=$nr_items) ? 'inline':'none') ]).'
             </a>
-            </div></p></div>';
+            </div></div>';
             $renderer->setElementTemplate($template_title, 'components['.$i.'][title]');
             $renderer->setElementTemplate($template_percentage, 'components['.$i.'][percentage]');
             $renderer->setElementTemplate($template_acronym, 'components['.$i.'][acronym]');
@@ -160,7 +160,6 @@ class GradeModel extends Model
                 $form->addRule('components['.$i.'][title]', get_lang('ThisFieldIsRequired'), 'required');
             }
             $form->addRule('components['.$i.'][percentage]', get_lang('OnlyNumbers'), 'numeric');
-
             $form->addRule(array('components['.$i.'][percentage]', 'maxvalue'), get_lang('Over100'), 'compare', '<=');
             $form->addRule(array('components['.$i.'][percentage]', 'minvalue'), get_lang('UnderMin'), 'compare', '>=');
 

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

@@ -300,7 +300,8 @@ function who_is_online($from, $number_of_items, $column = null, $direction = nul
     }
 
 	//This query will show all registered users. Only for dev purposes.
-	/*$query = "SELECT DISTINCT u.id as login_user_id, login_date FROM ".$track_online_table ."  e , $table_user u
+	/*$query = "SELECT DISTINCT u.id as login_user_id, login_date
+	        FROM $track_online_table e, $table_user u
             GROUP by u.id
             ORDER BY $column $direction
             LIMIT $from, $number_of_items";*/

+ 22 - 0
main/inc/lib/pear/HTML/QuickForm/Rule/MinText.php

@@ -0,0 +1,22 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+/**
+ * QuickForm rule to check a text field has a minimum of X chars
+ * @package chamilo.include
+ */
+class Html_Quickform_Rule_MinText extends HTML_QuickForm_Rule
+{
+    /**
+     * Function to check a text field has a minimum of X chars
+     * @see HTML_QuickForm_Rule
+     * @param string $text A text
+     * @param int $count The minimum number of characters that the text should contain
+     * @return boolean True if text has the minimum number of chars required
+     */
+    function validate($text, $count)
+    {
+        $checkMinText = create_function('$a,$b', 'return strlen(utf8_decode($a)) >= $b;');
+        return $checkMinText($text, $count);
+    }
+}

+ 2 - 2
main/inc/lib/pear/HTML/QuickForm/RuleRegistry.php

@@ -151,7 +151,8 @@ class HTML_QuickForm_RuleRegistry
             'maxfilesize', 'HTML_QuickForm_Rule_MaxFileSize',
             'mimetype', 'HTML_QuickForm_Rule_MimeType',
             'filename', 'HTML_QuickForm_Rule_FileName',
-            'validquestiontype' => 'HTML_QuickForm_Rule_QuestionType'
+            'validquestiontype' => 'HTML_QuickForm_Rule_QuestionType',
+            'mintext' => 'Html_Quickform_Rule_MinText'
         );
 
         $class = $rules[$ruleName];
@@ -192,7 +193,6 @@ class HTML_QuickForm_RuleRegistry
 
             return ($result == 0) ? false : $result;
         } else {
-
             return $rule->validate($values, $options);
         }
     } // end func validate

+ 9 - 9
main/inc/lib/search/tool_processors/document_processor.class.php

@@ -1,13 +1,10 @@
 <?php
-
 /* For licensing terms, see /license.txt */
+
 /**
  *
  * @package chamilo.include.search
  */
-/**
- * Code
- */
 include_once dirname(__FILE__) . '/../../../global.inc.php';
 require_once dirname(__FILE__) . '/search_processor.class.php';
 
@@ -15,13 +12,15 @@ require_once dirname(__FILE__) . '/search_processor.class.php';
  * Process documents before pass it to search listing scripts
  * @package chamilo.include.search
  */
-class document_processor extends search_processor {
-
-    function document_processor($rows) {
+class document_processor extends search_processor
+{
+    public function __construct($rows)
+    {
         $this->rows = $rows;
     }
 
-    public function process() {
+    public function process()
+    {
         $results = array();
         foreach ($this->rows as $row_val) {
             $search_show_unlinked_results = (api_get_setting('search_show_unlinked_results') == 'true');
@@ -67,7 +66,8 @@ class document_processor extends search_processor {
     /**
      * Get document information
      */
-    private function get_information($course_id, $doc_id) {
+    private function get_information($course_id, $doc_id)
+    {
         $course_information = api_get_course_info($course_id);
         $course_id = $course_information['real_id'];
         $course_path = $course_information['path'];

+ 19 - 12
main/inc/lib/search/tool_processors/learnpath_processor.class.php

@@ -1,13 +1,10 @@
 <?php
-
 /* For licensing terms, see /license.txt */
+
 /**
  *
  * @package chamilo.include.search
  */
-/**
- * Code
- */
 include_once dirname(__FILE__) . '/../../../global.inc.php';
 require_once dirname(__FILE__) . '/search_processor.class.php';
 require_once dirname(__FILE__) . '/../IndexableChunk.class.php';
@@ -16,11 +13,12 @@ require_once dirname(__FILE__) . '/../IndexableChunk.class.php';
  * Process learning paths before pass it to search listing scripts
  * @package chamilo.include.search
  */
-class learnpath_processor extends search_processor {
-
+class learnpath_processor extends search_processor
+{
     public $learnpaths = array();
 
-    function learnpath_processor($rows) {
+    public function __construct($rows)
+    {
         $this->rows = $rows;
         // group by learning path
         foreach ($rows as $row_id => $row_val) {
@@ -40,16 +38,24 @@ class learnpath_processor extends search_processor {
         }
     }
 
-    public function process() {
+    /**
+     * @return array
+     */
+    public function process()
+    {
         $results = array();
         foreach ($this->learnpaths as $courseid => $learnpaths) {
-            $search_show_unlinked_results = (api_get_setting('search_show_unlinked_results') == 'true');
-            $course_visible_for_user = api_is_course_visible_for_user(NULL, $courseid);
+            $search_show_unlinked_results = api_get_setting('search_show_unlinked_results') == 'true';
+            $course_visible_for_user = api_is_course_visible_for_user(null, $courseid);
             // can view course?
             if ($course_visible_for_user || $search_show_unlinked_results) {
                 foreach ($learnpaths as $lp_id => $lp) {
                     // is visible?
-                    $visibility = api_get_item_visibility(api_get_course_info($courseid), TOOL_LEARNPATH, $lp_id);
+                    $visibility = api_get_item_visibility(
+                        api_get_course_info($courseid),
+                        TOOL_LEARNPATH,
+                        $lp_id
+                    );
                     if ($visibility) {
                         list($thumbnail, $image, $name, $author) = $this->get_information($courseid, $lp_id, $lp['has_document_id']);
                         $url = api_get_path(WEB_CODE_PATH) . 'lp/lp_controller.php?cidReq=%s&action=view&lp_id=%s';
@@ -88,7 +94,8 @@ class learnpath_processor extends search_processor {
     /**
      * Get learning path information
      */
-    private function get_information($course_id, $lp_id, $has_document_id = TRUE) {
+    private function get_information($course_id, $lp_id, $has_document_id = true)
+    {
         $course_information = api_get_course_info($course_id);
         $course_id = $course_information['real_id'];
         $course_path = $course_information['path'];

+ 9 - 11
main/inc/lib/search/tool_processors/link_processor.class.php

@@ -1,13 +1,10 @@
 <?php
-
 /* For licensing terms, see /license.txt */
+
 /**
  *
  * @package chamilo.include.search
  */
-/**
- * Code
- */
 include_once dirname(__FILE__) . '/../../../global.inc.php';
 require_once dirname(__FILE__) . '/search_processor.class.php';
 
@@ -15,11 +12,12 @@ require_once dirname(__FILE__) . '/search_processor.class.php';
  * Process links before pass it to search listing scripts
  * @package chamilo.include.search
  */
-class link_processor extends search_processor {
-
+class link_processor extends search_processor
+{
     public $links = array();
 
-    function link_processor($rows) {
+    public function __construct($rows)
+    {
         $this->rows = $rows;
 
         // group all links together
@@ -37,9 +35,9 @@ class link_processor extends search_processor {
         }
     }
 
-    public function process() {
+    public function process()
+    {
         $results = array();
-
         foreach ($this->links as $courseCode => $one_course_links) {
             $course_info = api_get_course_info($courseCode);
             $search_show_unlinked_results = (api_get_setting('search_show_unlinked_results') == 'true');
@@ -95,7 +93,8 @@ class link_processor extends search_processor {
     /**
      * Get document information
      */
-    private function get_information($course_id, $link_id) {
+    private function get_information($course_id, $link_id)
+    {
         $course_information = api_get_course_info($course_id);
         $course_id = $course_information['real_id'];
         $course_id_alpha = $course_information['id'];
@@ -126,5 +125,4 @@ class link_processor extends search_processor {
             return array();
         }
     }
-
 }

+ 7 - 9
main/inc/lib/search/tool_processors/quiz_processor.class.php

@@ -1,13 +1,10 @@
 <?php
-
 /* For licensing terms, see /license.txt */
+
 /**
  *
  * @package chamilo.include.search
  */
-/**
- * Code
- */
 include_once dirname(__FILE__) . '/../../../global.inc.php';
 require_once dirname(__FILE__) . '/search_processor.class.php';
 
@@ -15,11 +12,12 @@ require_once dirname(__FILE__) . '/search_processor.class.php';
  * Process exercises before pass it to search listing scripts
  * @package chamilo.include.search
  */
-class quiz_processor extends search_processor {
-
+class quiz_processor extends search_processor
+{
     public $exercices = array();
 
-    function quiz_processor($rows) {
+    public function __construct($rows)
+    {
         $this->rows = $rows;
         // group by exercise
         foreach ($rows as $row_id => $row_val) {
@@ -55,10 +53,10 @@ class quiz_processor extends search_processor {
                     break;
             }
         }
-        //print_r($this->exercises);
     }
 
-    public function process() {
+    public function process()
+    {
         $results = array();
         foreach ($this->exercises as $courseid => $exercises) {
             $search_show_unlinked_results = (api_get_setting('search_show_unlinked_results') == 'true');

+ 2 - 2
main/inc/lib/search/tool_processors/search_processor.class.php

@@ -17,8 +17,8 @@
  * See lp_list_search for an example of calling the process.
  * @package chamilo.include.search
  */
-abstract class search_processor {
-
+abstract class search_processor
+{
     /**
      * Search engine api results
      */

+ 8 - 2
main/inc/lib/sessionmanager.lib.php

@@ -1583,6 +1583,7 @@ class SessionManager
         $tbl_item_properties = Database::get_course_table(TABLE_ITEM_PROPERTY);
         $tbl_student_publication = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
         $tbl_student_publication_assignment = Database :: get_course_table(TABLE_STUDENT_PUBLICATION_ASSIGNMENT);
+        $ticket = Database::get_main_table(TABLE_TICKET_TICKET);
         $em = Database::getManager();
 
         $userId = api_get_user_id();
@@ -1641,7 +1642,11 @@ class SessionManager
         Database::query("DELETE FROM $tbl_item_properties WHERE session_id IN ($id_checked)");
         Database::query("DELETE FROM $tbl_url_session WHERE session_id IN($id_checked)");
 
-        Database::query("DELETE FROM $tbl_session WHERE id IN ($id_checked)");
+        $sql = "UPDATE $ticket SET session_id = NULL WHERE session_id IN ($id_checked)";
+        Database::query($sql);
+
+        $sql = "DELETE FROM $tbl_session WHERE id IN ($id_checked)";
+        Database::query($sql);
 
         $extraFieldValue = new ExtraFieldValue('session');
         $extraFieldValue->deleteValuesByItem($id_checked);
@@ -1672,7 +1677,8 @@ class SessionManager
     {
         $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
         $id_promotion = intval($id_promotion);
-        $sql = "UPDATE $tbl_session SET promotion_id=0
+        $sql = "UPDATE $tbl_session 
+                SET promotion_id = 0
                 WHERE promotion_id = $id_promotion";
         if (Database::query($sql)) {
             return true;

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

@@ -986,12 +986,18 @@ class Template
         $this->assign('message_link', $message_link);
         $this->assign('message_url', $message_url);
 
-        //Certificate Link
-        $certificateUrl = null;
-        $certificateUrl = api_get_path(WEB_CODE_PATH).'gradebook/my_certificates.php';
-        $certificateLink = Display::url(get_lang('MyCertificates'), $certificateUrl);
-        $this->assign('certificate_link', $certificateLink);
-        $this->assign('certificate_url', $certificateUrl);
+        // Certificate Link
+
+        $allow = api_get_configuration_value('hide_my_certificate_link');
+        if ($allow === false) {
+            $certificateUrl = api_get_path(WEB_CODE_PATH).'gradebook/my_certificates.php';
+            $certificateLink = Display::url(
+                get_lang('MyCertificates'),
+                $certificateUrl
+            );
+            $this->assign('certificate_link', $certificateLink);
+            $this->assign('certificate_url', $certificateUrl);
+        }
 
         $institution = api_get_setting('Institution');
         $portal_name = empty($institution) ? api_get_setting('siteName') : $institution;

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

@@ -436,6 +436,7 @@ class UserManager
                 $tplContent->assign('login_name', $loginName);
                 $tplContent->assign('original_password', stripslashes($original_password));
                 $tplContent->assign('mailWebPath', $url);
+                $tplContent->assign('new_user', $user);
 
                 // ofaj
                 $urlSearch = api_get_path(WEB_PATH).'search.php';

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

@@ -360,22 +360,29 @@ class IndexManager
      */
     public function return_skills_links()
     {
-        $content = '';
-        $content .= '<ul class="nav nav-pills nav-stacked">';
+        $content = '<ul class="nav nav-pills nav-stacked">';
         /**
          * Generate the block for show a panel with links to My Certificates and Certificates Search pages
          * @return string The HTML code for the panel
          */
-        $certificatesItem = null;
-
+        $certificatesItem = '';
         if (!api_is_anonymous()) {
-            $certificatesItem = Display::tag(
-                'li',
-                Display::url(Display::return_icon('graduation.png',get_lang('MyCertificates'),null,ICON_SIZE_SMALL).
-                    get_lang('MyCertificates'),
-                    api_get_path(WEB_CODE_PATH) . "gradebook/my_certificates.php"
-                )
-            );
+            $allow = api_get_configuration_value('hide_my_certificate_link');
+            if ($allow === false) {
+                $certificatesItem = Display::tag(
+                    'li',
+                    Display::url(
+                        Display::return_icon(
+                            'graduation.png',
+                            get_lang('MyCertificates'),
+                            null,
+                            ICON_SIZE_SMALL
+                        ).
+                        get_lang('MyCertificates'),
+                        api_get_path(WEB_CODE_PATH)."gradebook/my_certificates.php"
+                    )
+                );
+            }
         }
 
         $searchItem = null;
@@ -407,15 +414,20 @@ class IndexManager
         }
 
         if (empty($certificatesItem) && empty($searchItem)) {
-            return null;
+            return '';
         }else{
             $content.= $certificatesItem;
             $content.= $searchItem;
         }
 
         if (api_get_setting('allow_skills_tool') == 'true') {
-
-            $content .= Display::tag('li', Display::url(Display::return_icon('skill-badges.png',get_lang('MySkills'),null,ICON_SIZE_SMALL).get_lang('MySkills'), api_get_path(WEB_CODE_PATH).'social/my_skills_report.php'));
+            $content .= Display::tag(
+                'li',
+                Display::url(
+                    Display::return_icon('skill-badges.png',get_lang('MySkills'),null,ICON_SIZE_SMALL).get_lang('MySkills'),
+                    api_get_path(WEB_CODE_PATH).'social/my_skills_report.php'
+                )
+            );
             $allowSkillsManagement = api_get_setting('allow_hr_skills_management') == 'true';
             if (($allowSkillsManagement && api_is_drh()) || api_is_platform_admin()) {
                 $content .= Display::tag('li',
@@ -424,6 +436,7 @@ class IndexManager
                         api_get_path(WEB_CODE_PATH) . 'admin/skills_wheel.php'));
             }
         }
+
         $content .= '</ul>';
         $html = self::show_right_block(
             get_lang("Skills"),
@@ -1541,6 +1554,7 @@ class IndexManager
      */
     public function return_hot_courses()
     {
+        // ofaj 4 courses
         return CourseManager::return_hot_courses(30, 4);
     }
 

+ 4 - 0
main/install/configuration.dist.php

@@ -249,3 +249,7 @@ $_configuration['system_stable'] = NEW_VERSION_STABLE;
 //$_configuration['editor_driver_list'] = ['PersonalDriver', 'CourseDriver'];
 // Hide send to hrm users options in announcements
 //$_configuration['announcements_hide_send_to_hrm_users'] = true;
+// Hide certificate link in index/userportal pages
+//$_configuration['hide_my_certificate_link'] = false;
+// Send only quiz answer notifications to course coaches and not general coach
+//$_configuration['block_quiz_mail_notification_general_coach'] = false;

+ 3 - 11
main/install/index.php

@@ -134,7 +134,7 @@ $update_from_version_8 = array(
     '1.10.2',
     '1.10.4',
     '1.10.6',
-    '1.10.8',
+    '1.10.8'
 );
 
 $my_old_version = '';
@@ -159,23 +159,15 @@ if (is_dir($oldSymfonyFolder)) {
 // A protection measure for already installed systems.
 if (isAlreadyInstalledSystem()) {
     // The system has already been installed, so block re-installation.
-    /*$global_error_code = 6;
+    $global_error_code = 6;
     require '../inc/global_error_message.inc.php';
-    die();*/
+    exit;
 }
 
 /* STEP 1 : INITIALIZES FORM VARIABLES IF IT IS THE FIRST VISIT */
 
 // Is valid request
 $is_valid_request = isset($_REQUEST['is_executable']) ? $_REQUEST['is_executable'] : null;
-/*foreach ($_POST as $request_index => $request_value) {
-    if (substr($request_index, 0, 4) == 'step') {
-        if ($request_index != $is_valid_request) {
-            unset($_POST[$request_index]);
-        }
-    }
-}*/
-
 $badUpdatePath = false;
 $emptyUpdatePath = true;
 $proposedUpdatePath = '';

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

@@ -2276,7 +2276,6 @@ function fixIds(EntityManager $em)
         $result = $connection->fetchAll($sql);
 
         foreach ($result as $item) {
-            //$courseId = $item['c_id'];
             $sessionId = intval($item['session_id']);
             $groupId = intval($item['to_group_id']);
             $iid = $item['iid'];
@@ -2404,11 +2403,6 @@ function fixIds(EntityManager $em)
                 continue;
             }
 
-            /*$group['description'] = Database::escape_string($group['description']);
-            $group['name'] = Database::escape_string($group['name']);
-            $sql = "INSERT INTO usergroup (name, group_type, description, picture, url, visibility, updated_at, created_at)
-                    VALUES ('{$group['name']}', '1', '{$group['description']}', '{$group['picture_uri']}', '{$group['url']}', '{$group['visibility']}', '{$group['updated_on']}', '{$group['created_on']}')";
-            */
             $params = [
                 'name' => $group['name'],
                 'description' => $group['description'],
@@ -2957,9 +2951,9 @@ function migrateSwitch($fromVersion, $manager, $processFiles = true)
             );
 
             if ($result) {
-                error_log('Migrations files were executed.');
-
+                error_log('Migrations files were executed ('.date('Y-m-d H:i:s').')');
                 fixIds($manager);
+                error_log('fixIds finished ('.date('Y-m-d H:i:s').')');
 
                 $connection->executeQuery("UPDATE settings_current SET selected_value = '1.10.0' WHERE variable = 'chamilo_database_version'");
 

+ 48 - 0
main/lang/brazilian/trad4all.inc.php

@@ -7758,4 +7758,52 @@ $TicketMsgWelcome = "Bem-vindo à seção de seus bilhetes. Aqui, você vai ser
 $TicketNoHistory = "Sem histórico";
 $RecalculateResults = "Recalcular os resultados";
 $XParenthesisDeleted = "%s (deleted)";
+$AvailableAlsoInMainPortal = "Disponível também no portal principal";
+$EditCourseCategoryToURL = "Editar categorias de curso para uma URL";
+$VisibleToSelf = "Visível para self";
+$VisibleToOthers = "Visível para os outros";
+$UpgradeVersion = "Atualizar a versão Chamilo LMS";
+$CRSTablesIntro = "O script de instalação detectou tabelas provenientes de versões anteriores que poderiam causar problemas durante o processo de upgrade. Por favor clique no botão abaixo para excluí-los. Nós fortemente recomendo que você faça um backup completo deles antes de confirmar esta última etapa de instalação.";
+$Removing = "Remoção";
+$CheckForCRSTables = "Verificar a existência de tabelas de versões anteriores";
+$YourPasswordCannotBeTheSameAsYourEmail = "Sua senha não pode ser o mesmo que seu e-mail";
+$YourPasswordCannotContainYourUsername = "Sua senha não pode conter seu nome de usuário";
+$WordTwoCharacterClasses = "Use diferentes classes de personagens";
+$TooManyRepetitions = "Muitas repetições";
+$YourPasswordContainsSequences = "Sua senha contém sequências";
+$PasswordVeryWeak = "Muito fraco";
+$UserXHasBeenAssignedToBoss = "Você foi designado o aluno %s";
+$UserXHasBeenAssignedToBossWithUrlX = "Você foi designado como tutor para o aprendiz %s. Você pode acessar seu perfil aqui: %s";
+$ShortName = "Nome curto";
+$Portal = "Portal";
+$CourseUserDocument = "Documento de usuário Course";
+$Destination = "Destino";
+$UserTestingEMailConf = "Teste de usuário de configuração de e-mail";
+$CMS = "CMS";
+$SendLegalSubject = "Condições legais";
+$SendLegalDescriptionToUrlX = "Por favor, aceitem nossas condições legais aqui: %s";
+$ExerciseInvisibleInSession = "Exercício invisível em sessões";
+$YouNeedToConfirmYourAgreementCheckYourEmail = "Você precisa confirmar o seu acordo com os nossos termos de primeira. Por favor verifique seu e-mail.";
+$ErrorImportingFile = "Arquivo de importação de erro";
+$Hidden = "Escondido";
+$Imported = "Importado";
+$ImportAsCSV = "Importar de um arquivo CSV";
+$EditThread = "Editar lista de discussão";
+$AddFiles = "Adicionar arquivos";
+$GroupForums = "Fóruns de grupos";
+$Generate = "Gerar";
+$Ticket = "Ticket";
+$InvalidApiKey = "Chave de API inválida";
+$NoAnnouncement = "Nenhum anúncio";
+$NoForum = "No fórum";
+$GoToExercise = "Ir para exercer";
+$ForumDissociate = "Dissociar fórum";
+$NoLPFound = "No caminho de aprendizagem encontrados";
+$ReportTypeLink = "Link de relatório ativo";
+$InviteesCantBeTutors = "Os convidados não podem ser tutores";
+$InvalidAction = "Ação inválida";
+$TicketWasThisAnswerSatisfying = "Essa resposta foi satisfatória?";
+$IfYouAreSureTheTicketWillBeClosed = "Se você estiver certo, o bilhete será fechada.";
+$Priorities = "Prioridades";
+$Tickets = "Tickets";
 ?>

+ 6 - 1
main/lang/english/trad4all.inc.php

@@ -3298,7 +3298,7 @@ $StartTimeWindow = "Start";
 $EndTimeWindow = "End";
 $AccessNotAllowed = "The access to this page is not allowed";
 $InThisCourse = "in this course";
-$ThisFieldIsRequired = "required field";
+$ThisFieldIsRequired = "Required field";
 $AllowedHTMLTags = "Allowed HTML tags";
 $FormHasErrorsPleaseComplete = "The form contains incorrect or incomplete data. Please check your input.";
 $StartDateShouldBeBeforeEndDate = "The first date should be before the end date";
@@ -7915,4 +7915,9 @@ $NoLPFound = "No learning path found";
 $ReportTypeLink = "Active report link";
 $InviteesCantBeTutors = "Invitees cannot be tutors";
 $InvalidAction = "Invalid action";
+$TicketWasThisAnswerSatisfying = "Was this answer satisfactory?";
+$IfYouAreSureTheTicketWillBeClosed = "If you are certain, the ticket will be closed.";
+$Priorities = "Priorities";
+$Tickets = "Tickets";
+$ThisTextShouldBeAtLeastXCharsLong = "This text should be at least %s characters long";
 ?>

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

@@ -7861,6 +7861,11 @@ $NoLPFound = "Aucun parcours trouvé";
 $ReportTypeLink = "Lien de rapport actif";
 $InviteesCantBeTutors = "Les invités ne peuvent être tuteurs";
 $InvalidAction = "Action incorrecte";
+$TicketWasThisAnswerSatisfying = "Selon vous, cette réponse est-elle satisfaisante?";
+$IfYouAreSureTheTicketWillBeClosed = "Si vous confirmez, le ticket sera clôturé.";
+$Priorities = "Priorités";
+$Tickets = "Tickets";
+$ThisTextShouldBeAtLeastXCharsLong = "Ce texte doit contenir au moins %s caractères";
 
 
 $TCCConnection = 'Connexion à TCC';

+ 56 - 0
main/lang/galician/trad4all.inc.php

@@ -7246,4 +7246,60 @@ $AttendanceUpdated = "Asistencias actualizadas";
 $HideHomeTopContentWhenLoggedInText = "Esconder o contido da páxina principal unha unha vez que se ten accedido";
 $HideHomeTopContentWhenLoggedInComment = "Esta opción permite esconder o bloque de introdución na páxina principal da plataforma (para deixar somente anunciós, por exemplo), para todos os usuarios que xa accederon ao sistema. O bloque de introdución xeral seguirá aparecendo para o público en xeral.";
 $HideGlobalAnnouncementsWhenNotLoggedInText = "Esconder anuncios globais para anónimos.";
+$HideGlobalAnnouncementsWhenNotLoggedInComment = "Esconder anuncios globais para anónimos";
+$CourseCreationUsesTemplateText = "Usar plantilla para novos cursos";
+$CourseCreationUsesTemplateComment = "Configure este parámetro para usar o mesmo curso plantilla (identificado polo seu ID numérico da base de datos) para todos os cursos que se crearán na plataforma en diante. Ollo que se o uso desta funcionalidade non está ben planificado, podería ter un impacto tremendo sobre o uso de espaczo en disco (por exemplo se o curso-plantilla contén arquivos moi pesados). O curso-plantilla  usarase como se un profesor estivera copiando un curso a través da ferramenta de mantemento de curso, polo que ningún dato de alumno se copiará, só o material creado polo profesor. Todas as demáis regras de copias de cursos aplican. Deixar este campo baleiro (o en 0) desactiva a funcionalidade.";
+$EnablePasswordStrengthCheckerText = "Validadr complexidade de contrasinal";
+$EnablePasswordStrengthCheckerComment = "Ao activar esta opción, aparecerá un indicador de complexidade de contrasinal cando o usuario cambie o seu contrasinal. Isto *NON* prohibe o ingreso dun mal contrasinal. Somente actúa como unha axuda visual.";
+$EnableCaptchaText = "CAPTCHA";
+$EnableCaptchaComment = "Ao activar esta opción, aparecerá un CAPTCHA no formulario de ingreso, para evitar os intentos de ingreso por forza bruta";
+$CaptchaNumberOfMistakesBeforeBlockingAccountText = "Mensaxe de erros en Captcha";
+$CaptchaNumberOfMistakesBeforeBlockingAccountComment = "Cantas veces un pode equivocarse ao ingresar o seu usuario e contrasinal co CAPTCHA antes de que a súa conta quede conxelada por un tempo.";
+$CaptchaTimeAccountIsLockedText = "Tempo bloqueado Captcha";
+$CaptchaTimeAccountIsLockedComment = "Se el usuario acada o máximo de veces que se pode equivocar de contrasinal (co CAPTCHA activado), a súa conta será conxelada (bloqueada) por esta cantidade de minutos.";
+$DRHAccessToAllSessionContentText = "Directores R.H. poden acceder ao contido de sesións";
+$DRHAccessToAllSessionContentComment = "Se activada, esta opción permite que un director de recursos humanos accedan a todo o contenido  e os usuarios das sesións que esté seguindo.";
+$ShowGroupForaInGeneralToolText = "Amosar os foros de grupo no foro xenral";
+$ShowGroupForaInGeneralToolComment = "Amosar os foros de grupo na ferramenta de foro a nivel do curso. Esta opción está habilitada por defecto (neste caso, as visibilidades individuais de cada foro de grupo seguen tendo efecto como criterio adicional). Se está desactivada, os foros de grupo só se verán na ferramenta de grupo, que estén visibles ou non.";
+$TutorsCanAssignStudentsToSessionsText = "Titores poden asignar estudantes a sesións";
+$TutorsCanAssignStudentsToSessionsComment = "Cando está activada esta opción, os titores de cursos dentro das sesións poden inscribir novos usuarios ás súas sesións. Senón, esta opción só está dispoñible para administradores e administradores de sesión.";
+$UniqueAnswerImagePreferredSize200x150 = "As imaxes redimensionaranse (hacia arriba ou abaixo) a 200x150 pixeles. Para obter un mellor resultado visual, recomendamos subir soamente imaxes deste tamaño.";
+$AllowLearningPathReturnLinkTitle = "Amosar a ligazón de regreso ás leccións";
+$AllowLearningPathReturnLinkComment = "Desactivar esta opción para esconder o botón 'Regresar á páxina principal' dentro das leccións.";
+$HideScormExportLinkTitle = "Esconder exportación a PDF en Leccións";
+$HideScormExportLinkComment = "Activar esta opción para esconder a opción de exportación a PDF dentro da lista de leccións.";
+$HideScormCopyLinkTitle = "Activar esta opción para esconder a opción de copia dentro da lista de leccións.";
+$HideScormCopyLinkComment = "Activar esta opción para esconder a opción de exportación SCORM dentro da lista de leccións.";
+$HideScormPdfLinkTitle = "Esconder exportación a PDF de Leccións";
+$HideScormPdfLinkComment = "Activar esta opción para esconder a opción de exportación a PDF dentro da lista de leccións.";
+$SessionDaysBeforeCoachAccessTitle = "Días de acceso previo a Leccións para titores";
+$SessionDaysBeforeCoachAccessComment = "A cantidade de días previos ao inicio da sesión durante os cales o titor pode acceder á sesión.";
+$SessionDaysAfterCoachAccessTitle = "Días de acceso do titor posteriores á fin da sesión";
+$SessionDaysAfterCoachAccessComment = "Cantidade por defecto de días posteriores á data de fin da sesión, durante os cales o titor pode seguir accedendo a esta sesión.";
+$PdfLogoHeaderTitle = "Logo de cabeceria PDF";
+$PdfLogoHeaderComment = "Activar para usar a imaxe en css/themes/[sucss]/images/pdf_logo_header.png como o logo de cabeceira de todas as exportacións en formato PDF (en vez do logo normal do portal)";
+$OrderUserListByOfficialCodeTitle = "Ordenar usuarios por código oficial";
+$OrderUserListByOfficialCodeComment = "Usar o campo de código oficial para reordenar a maioría das listas de usuario na plataforma, no canto de usar o seu nome ou apelido.";
+$AlertManagerOnNewQuizTitle = "Notificación de resposta de exercicio por correo";
+$AlertManagerOnNewQuizComment = "Valor por defecto para o envío dun correo cando un exercicio hafoi completado por un alumno. Este parámetro ven por defecto para cada novo curso, pero o profesor pode cambialo no seu propio curso individualmente.";
+$ShowOfficialCodeInExerciseResultListTitle = "Amosar o código oficial nos resultados dos exercicios";
+$ShowOfficialCodeInExerciseResultListComment = "Activar para amosar o código oficial nos resultados de exercicios.";
+$HidePrivateCoursesFromCourseCatalogTitle = "Esconder os cursos privados do catálogo";
+$HidePrivateCoursesFromCourseCatalogComment = "Activar para esconder os cursos privados do catálogo de curso. Este parámetro ten sentido cando se usa o catálogo só para permitir aos alumnos auto-inscribirse nos cursos (neste caso non ten sentido amosarlles cursos nos que non poden inscribirse).";
+$CoursesCatalogueShowSessionsTitle = "Catálogo de sesións e cursos";
+$CoursesCatalogueShowSessionsComment = "Mostrar alternativamente só os cursos, só as sesións, ou ambos no catálogo de cursos.";
+$AutoDetectLanguageCustomPagesTitle = "Auto-detección de idiomas en páxinas personalizadas";
+$AutoDetectLanguageCustomPagesComment = "Se usa as custom pages (páxinas de benvida personalizadas), active este parámetro se desexa activar o detector de idioma para presentar esta páxina de benvida no seu idioma. Desactive para usar o idioma por defecto da plataforma. A considerar se non ten os termos das páxinas personalizadas traducidos para todos os idiomas, por exemplo.";
+$LearningPathShowReducedReportTitle = "Leccións: mostrar informe reducido";
+$LearningPathShowReducedReportComment = "Dentro da ferramenta de leccións, cando un usuario revisa o seu propio progreso (a través da icona de estatísticas), mostrar unha versión máis curta (menos detallada) do informe de progreso.";
+$AllowSessionCourseCopyForTeachersTitle = "Copia sesión-a-sesión para titores";
+$AllowSessionCourseCopyForTeachersComment = "Activar esta opción para permitir aos titores de cursos dentro de sesións copiar o seu contido dentro doutro curso doutra sesión. Por defecto, esta funcionalidade está desactivada e só os administradores da plataforma poden usala.";
+$HideLogoutButtonTitle = "Esconder o botón de logout";
+$HideLogoutButtonComment = "Activar para esconder o botón de logout. Esta opción é util únicamente no caso de uso dun método externo de login e logout, por exemplo usando un mecanismo de Single Sign On dalgún tipo.";
+$RedirectAdminToCoursesListTitle = "Redirixir o \"admin\" á lista de cursos";
+$RedirectAdminToCoursesListComment = "O comportamento por defecto é mandar ao administrador directamente ao panel de administración despois do login (mentras os profesores e alumnos van á lista de cursos ou á páxina principal da plataforma). Activar para redirigir ao administrador tamén á súa lista de cursos.";
+$CourseImagesInCoursesListTitle = "Iconas de cursos personalizados";
+$CourseImagesInCoursesListComment = "Usar as imaxes de cursos como iconas nas listas de cursos (no canto da icona verde por defecto)";
+$StudentPublicationSelectionForGradebookTitle = "Tarefas consideradas para avaliación";
+$StudentPublicationSelectionForGradebookComment = "Na ferramenta de tarefas, os estudantes poden subir máis dun arquivo. No caso de haber máis dun arquivo do mesmo estudante para unha soa tarefa, cal destes debe ser considerado para a nota nas avaluacións? Isto depende da súa metodoloxía. Seleccione 'primeiro' para poñer o acento sobre a atención ao detalle (como entregar a tempo e o traballo finalizado a primeira vez). Use 'último' para poner o acento sobre o traballo colaborativo e a adaptabilidade.";
 ?>

+ 11 - 0
main/lang/slovak/trad4all.inc.php

@@ -3191,6 +3191,7 @@ $ModifyQuestion = "Zmeniť otázku";
 $Example = "Príklad";
 $CheckAll = "Zaškrtnúť všetko";
 $NbAnnoucement = "Oznam";
+$DisplayCertificate = "Zobraziť certifikát";
 $Doc = "Dokument";
 $PlataformAdmin = "Administrátor platformy";
 $Groups = "Skupiny";
@@ -7580,4 +7581,14 @@ $RateTheSkillInPractice = "Na stupnici od 1 do 10, ako dobre ste pozorovali, že
 $AverageRatingX = "Priemerné hodnotenie %s";
 $AverageRating = "Priemerné bodovanie";
 $GradebookTeacherResultsNotShown = "Výsledky učitelia nie sú zobrazené, nebudú sa brať do úvahy pri zadávaní známok.";
+$EmptyTemplate = "Prázdna šablóna";
+$Beta = "Beta";
+$Overwrite = "Prepísať";
+$UpdateLogo = "Aktualizovať logo";
+$DatabaseManager = "Správca databázy";
+$AddProfile = "Pridať profil";
+$VideoConference = "Videokonferencia";
+$TranslateThisTerm = "Preložiť tento pojem";
+$Template = "Šablóna";
+$CMS = "CMS";
 ?>

+ 6 - 1
main/lang/spanish/trad4all.inc.php

@@ -7675,7 +7675,7 @@ $ThereAreUsersUsingThisLanguagesDisableItManually = "Hay usuarios que usan actua
 $MessagingAllowSendPushNotificationTitle = "Permitir enviar notificaciones Push a la aplicación móvil de Chamilo Messaging";
 $MessagingAllowSendPushNotificationComment = "Enviar notificaciones Push a través de Firebase Console de Google";
 $MessagingGDCProjectNumberTitle = "ID del remitente de Firebase Console para Mensajería en la Nube";
-$MessagingGDCProjectNumberComment = "Necesita registrar un proyecto en <a href=\"https://console.developers.google.com/\">Google Developer Console</a>";
+$MessagingGDCProjectNumberComment = "Necesita registrar un proyecto en <a href=\"https://console.firebase.google.com/\">Google Firebase Console</a>";
 $MessagingGDCApiKeyTitle = "Server key de Firebase Console para Mensajería en la Nube";
 $MessagingGDCApiKeyComment = "Server key (legacy token) de las credenciales del proyecto en Firebase Console";
 $Overwrite = "Sobreescribir";
@@ -7932,4 +7932,9 @@ $NoLPFound = "No hay lecciones";
 $ReportTypeLink = "Enlace de reporte activo";
 $InviteesCantBeTutors = "Los invitados no pueden ser tutores";
 $InvalidAction = "Acción incorrecta";
+$TicketWasThisAnswerSatisfying = "¿Fue satisfactoria la respuesta?";
+$IfYouAreSureTheTicketWillBeClosed = "Si está seguro, el ticket será cerrado.";
+$Priorities = "Prioridades";
+$Tickets = "Tickets";
+$ThisTextShouldBeAtLeastXCharsLong = "Este texto debe contener por lo mínimo 10 caracteres";
 ?>

+ 62 - 13
main/lp/learnpath.class.php

@@ -3408,7 +3408,6 @@ class learnpath
                         $item_id,
                         $this->get_view_id()
                     );
-
                     if ($this->debug > 0) {
                         error_log('rl_get_resource_link_for_learnpath - file: ' . $file, 0);
                     }
@@ -6533,7 +6532,16 @@ class learnpath
         $dir = $_SESSION['oLP']->display_item_form('dir', get_lang('EnterDataNewChapter'), 'add_item');
         echo Display::tabs(
             $headers,
-            array($documents, $exercises, $links, $works, $forums, $dir, $finish), 'resource_tab'
+            array(
+                $documents,
+                $exercises,
+                $links,
+                $works,
+                $forums,
+                $dir,
+                $finish,
+            ),
+            'resource_tab'
         );
 
         return true;
@@ -9087,11 +9095,39 @@ class learnpath
         require_once '../forum/forumfunction.inc.php';
         require_once '../forum/forumconfig.inc.php';
 
-        $a_forums = get_forums();
+        $forumCategories = get_forum_categories();
+        $forumsInNoCategory = get_forums_in_category(0);
+        if (!empty($forumsInNoCategory)) {
+            $forumCategories = array_merge(
+                $forumCategories,
+                array(
+                    array(
+                        'cat_id' => 0,
+                        'session_id' => 0,
+                        'visibility' => 1,
+                        'cat_comment' => null,
+                    ),
+                )
+            );
+        }
+
+        $forumList = get_forums();
+        $a_forums = [];
+        foreach ($forumCategories as $forumCategory) {
+            // The forums in this category.
+            $forumsInCategory = get_forums_in_category($forumCategory['cat_id']);
+            if (!empty($forumsInCategory)) {
+                foreach ($forumList as $forum) {
+                    if (isset($forum['forum_category']) && $forum['forum_category'] == $forumCategory['cat_id']) {
+                        $a_forums[] = $forum;
+                    }
+                }
+            }
+        }
 
         $return = '<ul class="lp_resource">';
 
-        //First add link
+        // First add link
         $return .= '<li class="lp_resource_element">';
         $return .= Display::return_icon('new_forum.png');
         $return .= Display::url(
@@ -9106,27 +9142,25 @@ class learnpath
         $return .= '</li>';
 
         $return .= '<script>
-            function toggle_forum(forum_id){
-                if(document.getElementById("forum_"+forum_id+"_content").style.display == "none"){
+            function toggle_forum(forum_id) {
+                if (document.getElementById("forum_"+forum_id+"_content").style.display == "none") {
                     document.getElementById("forum_"+forum_id+"_content").style.display = "block";
-                            document.getElementById("forum_"+forum_id+"_opener").src = "' . Display::returnIconPath('remove.gif').'";
+                    document.getElementById("forum_"+forum_id+"_opener").src = "' . Display::returnIconPath('remove.gif').'";
                 } else {
                     document.getElementById("forum_"+forum_id+"_content").style.display = "none";
-                            document.getElementById("forum_"+forum_id+"_opener").src = "' . Display::returnIconPath('add.gif').'";
+                    document.getElementById("forum_"+forum_id+"_opener").src = "' . Display::returnIconPath('add.gif').'";
                 }
             }
         </script>';
 
         foreach ($a_forums as $forum) {
             if (!empty($forum['forum_id'])) {
-
                 $link = Display::url(
                     Display::return_icon('preview_view.png', get_lang('Preview')),
                     api_get_path(WEB_CODE_PATH).'forum/viewforum.php?'.api_get_cidreq().'&forum='.$forum['forum_id'],
                     ['target' => '_blank']
                 );
 
-
                 $return .= '<li class="lp_resource_element" data_id="'.$forum['forum_id'].'" data_type="'.TOOL_FORUM.'" title="'.$forum['forum_title'].'" >';
                 $return .= '<a class="moved" href="#">';
                 $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
@@ -9144,7 +9178,6 @@ class learnpath
                 $a_threads = get_threads($forum['forum_id']);
                 if (is_array($a_threads)) {
                     foreach ($a_threads as $thread) {
-
                         $link = Display::url(
                             Display::return_icon('preview_view.png', get_lang('Preview')),
                             api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.api_get_cidreq().'&forum='.$forum['forum_id'].'&thread='.$thread['thread_id'],
@@ -11261,12 +11294,28 @@ EOD;
         }
         $row_item = Database::fetch_array($res_item, 'ASSOC');
 
+        $item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
+        // Get the lp_item_view with the highest view_count.
+        $sql = "SELECT * FROM $item_view_table
+                WHERE
+                    c_id = $course_id AND
+                    lp_item_id = " . $row_item['id'] . " AND
+                    lp_view_id = " . $lpViewId . "
+                ORDER BY view_count DESC";
+        $learnpathItemViewResult = Database::query($sql);
+        $learnpathItemViewData = Database::fetch_array($learnpathItemViewResult, 'ASSOC');
+        $learnpathItemViewId = 0;
+        if (isset($learnpathItemViewData)) {
+            $learnpathItemViewId = $learnpathItemViewData['id'];
+        }
+
         $type = strtolower($row_item['item_type']);
         $id = (strcmp($row_item['path'], '') == 0) ? '0' : $row_item['path'];
         $origin = 'learnpath';
         $main_dir_path = api_get_path(WEB_CODE_PATH);
         $main_course_path = api_get_path(WEB_COURSE_PATH).$course_info['directory'].'/';
         $link = '';
+
         switch ($type) {
             case 'dir':
                 $link .= $main_dir_path . 'lp/blank.php';
@@ -11288,11 +11337,11 @@ EOD;
                     $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
                     $sql = "SELECT * FROM $TBL_EXERCICES WHERE c_id = $course_id AND id=$id";
                     $result= Database::query($sql);
-                    $myrow=Database::fetch_array($result);
+                    $myrow = Database::fetch_array($result);
                     if ($row_item['title'] != '') {
                         $myrow['title'] = $row_item['title'];
                     }
-                    $link .= $main_dir_path . 'exercise/overview.php?cidReq='.$course_code.'&session_id='.$session_id.'&lp_init=1&origin='.$origin.'&learnpath_id='.$learningPathId.'&learnpath_item_id='.$id_in_path.'&exerciseId='.$id;
+                    $link .= $main_dir_path . 'exercise/overview.php?cidReq='.$course_code.'&session_id='.$session_id.'&lp_init=1&origin='.$origin.'&learnpath_item_view_id='.$learnpathItemViewId.'&learnpath_id='.$learningPathId.'&learnpath_item_id='.$id_in_path.'&exerciseId='.$id;
                 }
                 break;
             case 'hotpotatoes': //lowercase because of strtolower above

+ 3 - 3
main/lp/lp_view.php

@@ -1,6 +1,8 @@
 <?php
 /* For licensing terms, see /license.txt */
 
+use ChamiloSession as Session;
+
 /**
  * This file was originally the copy of document.php, but many modifications happened since then ;
  * the direct file view is not needed anymore, if the user uploads a scorm zip file, a directory
@@ -12,7 +14,6 @@
  * @author Isthvan Mandak, several new features
  * @author Roan Embrechts, code improvements and refactoring
  */
-use \ChamiloSession as Session;
 
 $use_anonymous = true;
 
@@ -34,10 +35,9 @@ $lp_id = !empty($_GET['lp_id']) ? intval($_GET['lp_id']) : 0;
 $sessionId = api_get_session_id();
 
 // Check if the learning path is visible for student - (LP requisites)
-
 if (!api_is_platform_admin()) {
     if (
-        !api_is_allowed_to_edit(null, true) &&
+        !api_is_allowed_to_edit(null, true, false, false) &&
         !learnpath::is_lp_visible_for_student($lp_id, api_get_user_id())
     ) {
         api_not_allowed(true);

+ 7 - 3
main/mySpace/lp_tracking.php

@@ -27,15 +27,19 @@ $csv_content = array();
 $course_info = api_get_course_info($courseCode);
 
 if (empty($course_info) || empty($lp_id)) {
-    api_not_allowed();
+    api_not_allowed(true);
 }
 $userInfo = api_get_user_info($user_id);
 $name = $userInfo['complete_name'];
 
+$isBoss = UserManager::userIsBossOfStudent(api_get_user_id(), $user_id);
+
 if (!api_is_platform_admin(true) &&
     !CourseManager :: is_course_teacher(api_get_user_id(), $courseCode) &&
-    !Tracking :: is_allowed_to_coach_student(api_get_user_id(), $user_id) && !api_is_drh() && !api_is_course_tutor()) {
-	api_not_allowed();
+    !$isBoss &&
+    !Tracking :: is_allowed_to_coach_student(api_get_user_id(), $user_id) && !api_is_drh() && !api_is_course_tutor()
+) {
+	api_not_allowed(true);
 }
 
 if ($origin == 'user_course') {

+ 11 - 15
main/mySpace/myStudents.php

@@ -315,6 +315,12 @@ if (isset($message)) {
 }
 
 $token = Security::get_token();
+
+$extraParam = '';
+if (api_is_student_boss()) {
+    $extraParam = '&log_as_user='.$student_id;
+}
+
 if (!empty($student_id)) {
     // Actions bar
     echo '<div class="actions">';
@@ -678,7 +684,7 @@ if (!empty($student_id)) {
 
             // Courses
             echo '<h3>'.$title.'</h3>';
-            echo '<div class="table-respondive">';
+            echo '<div class="table-responsive">';
             echo '<table class="table table-striped table-hover courses-tracking">';
             echo '<thead>';
             echo '<tr>
@@ -709,7 +715,7 @@ if (!empty($student_id)) {
                         if (!empty($results_faults_avg['total'])) {
                             if (api_is_drh()) {
                                 $attendances_faults_avg =
-                                    '<a title="'.get_lang('GoAttendance').'" href="'.api_get_path(WEB_CODE_PATH).'attendance/index.php?cidReq='.$courseCodeItem.'&id_session='.$sId.'&student_id='.$student_id.'">'.
+                                    '<a title="'.get_lang('GoAttendance').'" href="'.api_get_path(WEB_CODE_PATH).'attendance/index.php?cidReq='.$courseCodeItem.$extraParam.'&id_session='.$sId.'&student_id='.$student_id.'">'.
                                     $results_faults_avg['faults'].'/'.$results_faults_avg['total'].' ('.$results_faults_avg['porcent'].'%)</a>';
                             } else {
                                 $attendances_faults_avg =
@@ -757,11 +763,6 @@ if (!empty($student_id)) {
                             $scoretotal_display
                         );
 
-                        $extraParam = '';
-                        if (api_is_student_boss()) {
-                            $extraParam = '&log_as_user='.$student_id;
-                        }
-
                         echo '<tr>
                         <td ><a href="' .$courseInfoItem['course_public_url'] .'?id_session=' . $sId . $extraParam.'">'.
                             $courseInfoItem['title'].'</a></td>
@@ -772,10 +773,10 @@ if (!empty($student_id)) {
                         <td >'.$scoretotal_display.'</td>';
 
                         if (isset($_GET['id_coach']) && intval($_GET['id_coach']) != 0) {
-                            echo '<td width="10"><a href="'.api_get_self().'?student='.$user_info['user_id'].'&details=true&course='.$courseInfoItem['code'].'&id_coach='.Security::remove_XSS($_GET['id_coach']).'&origin='.$origin.'&id_session='.$sId.'#infosStudent">
+                            echo '<td width="10"><a href="'.api_get_self().'?student='.$user_info['user_id'].$extraParam.'&details=true&course='.$courseInfoItem['code'].'&id_coach='.Security::remove_XSS($_GET['id_coach']).'&origin='.$origin.'&id_session='.$sId.'#infosStudent">
                             '.Display::return_icon('2rightarrow.png', get_lang('Details')).'</a></td>';
                         } else {
-                            echo '<td width="10"><a href="'.api_get_self().'?student='.$user_info['user_id'].'&details=true&course='.$courseInfoItem['code'].'&origin='.$origin.'&id_session='.$sId.'#infosStudent">
+                            echo '<td width="10"><a href="'.api_get_self().'?student='.$user_info['user_id'].$extraParam.'&details=true&course='.$courseInfoItem['code'].'&origin='.$origin.'&id_session='.$sId.'#infosStudent">
                             '.Display::return_icon('2rightarrow.png', get_lang('Details')).'</a></td>';
                         }
                         echo '</tr>';
@@ -1016,7 +1017,7 @@ if (!empty($student_id)) {
                         }
                         $link = Display::url(
                             Display::return_icon('2rightarrow.png', get_lang('Details')),
-                            'lp_tracking.php?cidReq='.$course_code.'&course='.$course_code.$from.'&origin='.$origin.'&lp_id='.$learnpath->getId().'&student_id='.$user_info['user_id'].'&id_session='.$sessionId
+                            'lp_tracking.php?cidReq='.$course_code.'&course='.$course_code.$from.$extraParam.'&origin='.$origin.'&lp_id='.$learnpath->getId().'&student_id='.$user_info['user_id'].'&id_session='.$sessionId
                         );
                         echo Display::tag('td', $link);
                     }
@@ -1079,11 +1080,6 @@ if (!empty($student_id)) {
         $result_exercices = Database::query($sql);
         $i = 0;
 
-        $extraParam = '';
-        if (api_is_student_boss()) {
-            $extraParam = '&log_as_user='.$student_id;
-        }
-
         if (Database :: num_rows($result_exercices) > 0) {
             while ($exercices = Database :: fetch_array($result_exercices)) {
                 $exercise_id = intval($exercices['id']);

+ 7 - 6
main/social/group_waiting_list.php

@@ -34,9 +34,9 @@ if (empty($group_id)) {
     }
 }
 
-$interbreadcrumb[]= array ('url' =>'groups.php','name' => get_lang('Groups'));
+$interbreadcrumb[]= array ('url' => 'groups.php','name' => get_lang('Groups'));
 $interbreadcrumb[] = array('url' => 'group_view.php?id='.$group_id, 'name' => $group_info['name']);
-$interbreadcrumb[]= array ('url' =>'#','name' => get_lang('WaitingList'));
+$interbreadcrumb[]= array ('url' => '#','name' => get_lang('WaitingList'));
 
 // Group information
 $admins = $usergroup->get_users_by_group(
@@ -90,14 +90,15 @@ $new_member_list = array();
 $social_left_content = SocialManager::show_social_menu('waiting_list', $group_id);
 
 // Display form
-foreach($users as $user) {
+foreach ($users as $user) {
+    $userId = $user['user_info']['user_id'];
     switch ($user['relation_type']) {
         case GROUP_USER_PERMISSION_PENDING_INVITATION_SENT_BY_USER:
-            $user['link']  = '<a href="group_waiting_list.php?id='.$group_id.'&u='.$user['user_id'].'&action=accept">'.
+            $user['link']  = '<a href="group_waiting_list.php?id='.$group_id.'&u='.$userId.'&action=accept">'.
                 Display::return_icon('invitation_friend.png', get_lang('AddNormalUser')).'</a>';
-            $user['link'] .= '<a href="group_waiting_list.php?id='.$group_id.'&u='.$user['user_id'].'&action=set_moderator">'.
+            $user['link'] .= '<a href="group_waiting_list.php?id='.$group_id.'&u='.$userId.'&action=set_moderator">'.
                 Display::return_icon('social_moderator_add.png', get_lang('AddModerator')).'</a>';
-            $user['link'] .= '<a href="group_waiting_list.php?id='.$group_id.'&u='.$user['user_id'].'&action=deny">'.
+            $user['link'] .= '<a href="group_waiting_list.php?id='.$group_id.'&u='.$userId.'&action=deny">'.
                 Display::return_icon('user_delete.png', get_lang('DenyEntry')).'</a>';
             break;
     }

+ 0 - 6
main/template/default/chat/chat.tpl

@@ -78,12 +78,6 @@
                             friend: ChChat.currentFriend
                         })
                         .done(function (response) {
-                            if (response.data.chatIsDenied) {
-                                alert("{{ 'ChatDenied'|get_lang }}");
-
-                                return;
-                            }
-
                             if (response.data.history) {
                                 ChChat._historySize = response.data.oldFileSize;
                                 ChChat.setHistory(response.data.history);

+ 3 - 0
main/template/default/layout/menu.tpl

@@ -38,9 +38,12 @@
                             <a title="{{ "Inbox"|get_lang }}" href="{{ message_url }}">
                                 <em class="fa fa-envelope" aria-hidden="true"></em> {{ "Inbox"|get_lang }}
                             </a>
+                            {% if certificate_url %}
                             <a title="{{ "MyCertificates"|get_lang }}" href="{{ certificate_url }}">
                                 <em class="fa fa-graduation-cap" aria-hidden="true"></em> {{ "MyCertificates"|get_lang }}
                             </a>
+                            {% endif %}
+
                             <a id="logout_button" title="{{ "Logout"|get_lang }}" href="{{ logout_link }}" >
                                 <em class="fa fa-sign-out"></em> {{ "Logout"|get_lang }}
                             </a>

+ 4 - 2
main/template/default/social/whoisonline.tpl

@@ -3,6 +3,7 @@
 {% block content %}
     <div class="row">
         <div class="col-md-12">
+            {% if social_search %}
             <div class="search-user">
                 <div class="panel panel-default">
                     <div class="panel-heading">
@@ -13,6 +14,7 @@
                     </div>
                 </div>
             </div>
+            {% endif %}
             <div id="whoisonline">
                 <div id="user-list" class="row">
                     {{ whoisonline }}
@@ -29,7 +31,7 @@
                 page = $("#link_load_more_items").attr("data_link");
                 $.ajax({
                     beforeSend: function() {
-                        $("#link_load_more_items").html("{{ 'Loading' | get_lang }} <em class='fa fa-spinner fa-pulse fa-fw'></em>");
+                        $("#link_load_more_items").html("{{ 'Loading' | get_lang|escape('js') }} <em class='fa fa-spinner fa-pulse fa-fw'></em>");
                     },
                     type: "GET",
                     url: "main/inc/ajax/online.ajax.php?a=load_online_user",
@@ -38,7 +40,7 @@
                         if (data != "end") {
                             $("#link_load_more_items").attr("data_link", parseInt(page) + 1);
                             $("#user-list").append(data);
-                            $("#link_load_more_items").html("{{ 'More' | get_lang }}");
+                            $("#link_load_more_items").html("{{ 'More' | get_lang|escape('js')}}");
                         } else {
                             $("#link_load_more_items").remove();
                         }

+ 1 - 1
main/template/rainbow/gradebook/custom_certificate.tpl

@@ -2,7 +2,7 @@
 
 {{ complete_name }}
 
-{{ 'UserHasParticipateDansDePlatformeXTheContratDateXCertificateDateXTimeX' | get_lang | format(_s.site_name, terms_validation_date, certificate_generated_date, time_in_platform)}}
+{{ 'UserHasParticipateDansDePlatformeXTheContratDateXCertificateDateXTimeX' | get_lang | format(_s.site_name, certificate_generated_date, terms_validation_date, time_in_platform)}}
 
 {{ 'TheContentsAreValidated' | get_lang }}
 

+ 1 - 1
main/template/rainbow/layout/layout_2_col.tpl

@@ -61,7 +61,7 @@
                             <li><i class="fa fa-signal fa-4x icon-color" aria-hidden="true"></i></li>
                             <li>
                                 <h2 class="number">{{ total_exercises }}</h2>
-                                <p class="text">{{ "Certificates"|get_lang }}</p>
+                                <p class="text">{{ "Exercises"|get_lang }}</p>
                             </li>
                         </ul>
                     </div>

+ 5 - 3
plugin/bbb/admin.php

@@ -26,9 +26,11 @@ $meetings = $bbb->getMeetings(0, 0, 0, true);
 foreach ($meetings as &$meeting) {
     $participants = $bbb->findMeetingParticipants($meeting['id']);
 
-    /** @var User $participant */
-    foreach ($participants as $participant) {
-        $meeting['participants'][] = $participant['participant']->getCompleteName();
+    foreach ($participants as $meetingParticipant) {
+        /** @var User $participant */
+        $participant = $meetingParticipant['participant'];
+        $meeting['participants'][] = $participant->getCompleteName()
+            . ' (' . $participant->getEmail() . ')';
     }
 }
 

+ 23 - 17
plugin/bbb/lib/bbb.lib.php

@@ -247,7 +247,7 @@ class bbb
             }
         }
 
-        $params['attendee_pw'] = isset($params['moderator_pw']) ? $params['moderator_pw'] : $this->getUserMeetingPassword();
+        $params['attendee_pw'] = isset($params['attendee_pw']) ? $params['attendee_pw'] : $this->getUserMeetingPassword();
         $attendeePassword = $params['attendee_pw'];
         $params['moderator_pw'] = isset($params['moderator_pw']) ? $params['moderator_pw'] : $this->getModMeetingPassword();
         $moderatorPassword = $params['moderator_pw'];
@@ -574,7 +574,14 @@ class bbb
     public function getMeetings($courseId = 0, $sessionId = 0, $groupId = 0, $isAdminReport = false)
     {
         $em = Database::getManager();
-        $pass = $this->getUserMeetingPassword();
+
+        $manager = $this->isConferenceManager();
+        if ($manager) {
+            $pass = $this->getUserMeetingPassword();
+        } else {
+            $pass = $this->getModMeetingPassword();
+        }
+
         $conditions = [];
 
         if ($courseId || $sessionId || $groupId) {
@@ -665,7 +672,7 @@ class bbb
                     $record['playbackFormatUrl'] = $meetingDB['video_url'];
                 }
 
-                $recordLink = isset($record['playbackFormatUrl'])
+                $recordLink = isset($record['playbackFormatUrl']) && !empty($record['playbackFormatUrl'])
                     ? Display::url(
                         $this->plugin->get_lang('ViewRecord'),
                         $record['playbackFormatUrl'],
@@ -758,7 +765,12 @@ class bbb
             return false;
         }
         $meetingData = Database::select('*', $this->table, array('where' => array('id = ?' => array($id))), 'first');
-        $pass = $this->getUserMeetingPassword();
+        $manager = $this->isConferenceManager();
+        if ($manager) {
+            $pass = $this->getUserMeetingPassword();
+        } else {
+            $pass = $this->getModMeetingPassword();
+        }
 
         $endParams = array(
             'meetingId' => $meetingData['remote_id'],   // REQUIRED - We have to know which meeting to end.
@@ -778,23 +790,17 @@ class bbb
      */
     public function getUserMeetingPassword()
     {
-        if ($this->isConferenceManager()) {
-
-            return $this->getModMeetingPassword();
-        } else {
-
-            if ($this->isGlobalConferencePerUserEnabled()) {
-
-                return 'url_'.$this->userId.'_'.api_get_current_access_url_id();
-            }
+        if ($this->isGlobalConferencePerUserEnabled()) {
 
-            if ($this->isGlobalConference()) {
+            return 'url_'.$this->userId.'_'.api_get_current_access_url_id();
+        }
 
-                return 'url_'.api_get_current_access_url_id();
-            }
+        if ($this->isGlobalConference()) {
 
-            return api_get_course_id();
+            return 'url_'.api_get_current_access_url_id();
         }
+
+        return api_get_course_id();
     }
 
     /**

+ 0 - 2
plugin/bbb/start.php

@@ -21,9 +21,7 @@ $bbb = new bbb('', '', $isGlobal, $isGlobalPerUser);
 
 if ($bbb->pluginEnabled) {
     if ($bbb->isServerRunning()) {
-
         if (isset($_GET['launch']) && $_GET['launch'] == 1) {
-
             if (file_exists(__DIR__ . '/config.vm.php')) {
                 $config = require __DIR__ . '/config.vm.php';
                 $vmIsEnabled = true;

+ 1 - 0
src/Chamilo/CoreBundle/Entity/HookCall.php

@@ -1,4 +1,5 @@
 <?php
+/* For licensing terms, see /license.txt */
 
 namespace Chamilo\CoreBundle\Entity;
 

+ 1 - 0
src/Chamilo/CoreBundle/Entity/HookEvent.php

@@ -1,4 +1,5 @@
 <?php
+/* For licensing terms, see /license.txt */
 
 namespace Chamilo\CoreBundle\Entity;
 

+ 1 - 0
src/Chamilo/CoreBundle/Entity/HookObserver.php

@@ -1,4 +1,5 @@
 <?php
+/* For licensing terms, see /license.txt */
 
 namespace Chamilo\CoreBundle\Entity;
 

+ 1 - 0
src/Chamilo/CoreBundle/Entity/Language.php

@@ -1,4 +1,5 @@
 <?php
+/* For licensing terms, see /license.txt */
 
 namespace Chamilo\CoreBundle\Entity;
 

+ 3 - 2
src/Chamilo/CoreBundle/Entity/Legal.php

@@ -1,4 +1,5 @@
 <?php
+/* For licensing terms, see /license.txt */
 
 namespace Chamilo\CoreBundle\Entity;
 
@@ -80,8 +81,8 @@ class Legal
         $this->id = $id;
 
         return $this;
-    }   
-    
+    }
+
 
     /**
      * Set date

+ 10 - 11
src/Chamilo/CoreBundle/Entity/Message.php

@@ -1,4 +1,5 @@
 <?php
+/* For licensing terms, see /license.txt */
 
 namespace Chamilo\CoreBundle\Entity;
 
@@ -12,6 +13,15 @@ use Doctrine\ORM\Mapping as ORM;
  */
 class Message
 {
+    /**
+     * @var integer
+     *
+     * @ORM\Column(name="id", type="bigint")
+     * @ORM\Id
+     * @ORM\GeneratedValue()
+     */
+    private $id;
+
     /**
      * @var integer
      *
@@ -82,17 +92,6 @@ class Message
      */
     private $votes;
 
-    /**
-     * @var integer
-     *
-     * @ORM\Column(name="id", type="bigint")
-     * @ORM\Id
-     * @ORM\GeneratedValue(strategy="IDENTITY")
-     */
-    private $id;
-
-
-
     /**
      * Set userSenderId
      *

+ 1 - 0
src/Chamilo/CoreBundle/Entity/MessageAttachment.php

@@ -1,4 +1,5 @@
 <?php
+/* For licensing terms, see /license.txt */
 
 namespace Chamilo\CoreBundle\Entity;
 

+ 1 - 0
src/Chamilo/CoreBundle/Entity/Notification.php

@@ -1,4 +1,5 @@
 <?php
+/* For licensing terms, see /license.txt */
 
 namespace Chamilo\CoreBundle\Entity;
 

+ 1 - 0
src/Chamilo/CoreBundle/Entity/OpenidAssociation.php

@@ -1,4 +1,5 @@
 <?php
+/* For licensing terms, see /license.txt */
 
 namespace Chamilo\CoreBundle\Entity;
 

+ 57 - 0
src/Chamilo/CourseBundle/Entity/CQuiz.php

@@ -202,6 +202,21 @@ class CQuiz
      */
     private $questionSelectionType;
 
+    /**
+     * @var boolean
+     *
+     * @ORM\Column(name="hide_question_title", type="boolean", nullable=true)
+     */
+    private $hideQuestionTitle;
+
+    /**
+     * CQuiz constructor.
+     */
+    public function __construct()
+    {
+        $this->hideQuestionTitle = false;
+    }
+
     /**
      * Set title
      *
@@ -749,4 +764,46 @@ class CQuiz
     {
         return $this->cId;
     }
+
+    /**
+     * @return int
+     */
+    public function getQuestionSelectionType()
+    {
+        return $this->questionSelectionType;
+    }
+
+    /**
+     * @param int $questionSelectionType
+     * @return CQuiz
+     */
+    public function setQuestionSelectionType($questionSelectionType)
+    {
+        $this->questionSelectionType = $questionSelectionType;
+
+        return $this;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isHideQuestionTitle()
+    {
+        return $this->hideQuestionTitle;
+    }
+
+    /**
+     * @param boolean $hideQuestionTitle
+     * @return CQuiz
+     */
+    public function setHideQuestionTitle($hideQuestionTitle)
+    {
+        $this->hideQuestionTitle = $hideQuestionTitle;
+
+        return $this;
+    }
+
+
+
+
 }

+ 4 - 4
src/Chamilo/CourseBundle/Entity/CStudentPublicationRelUser.php

@@ -32,16 +32,16 @@ class CStudentPublicationRelUser
     /**
      * @var integer
      *
-     * @ORM\Column(name="c_id", type="integer")
+     * @ORM\Column(name="id", type="integer", nullable=true)
      */
-    private $cId;
+    private $id;
 
     /**
      * @var integer
      *
-     * @ORM\Column(name="id", type="integer", nullable=true)
+     * @ORM\Column(name="c_id", type="integer")
      */
-    private $id;
+    private $cId;
 
     /**
      * @var integer

+ 0 - 42
tests/main/inc/lib/main_api.lib.test.php

@@ -392,18 +392,6 @@ class TestMainApi extends UnitTestCase {
 		$this->assertEqual($res,'');
 		ob_end_clean();
 	}
-/*
-	function testConvertMysqlDate(){
-	 	$last_post_datetime = array();
-	 	$res=convert_mysql_date($last_post_datetime);
-	 	$this->assertTrue($res);
-	}
-*/
-	function testApiGetDatetime(){
-	 	$res=api_get_datetime($time=null);
-	 	$this->assertTrue($res);
-	 	$this->assertFalse(isset($time));
-	}
 
 	function testApiGetItemVisibility(){
 	 	global $_course;
@@ -552,26 +540,12 @@ class TestMainApi extends UnitTestCase {
 		$this->assertTrue(is_array($res));
 	}
 
-	function testApiAddAccessUrl(){
-		$u = Database::escape_string($u);
-		$d = Database::escape_string($d);
-		$res = api_add_access_url($u,$d='',$a=1);
-		$this->assertTrue(is_numeric($res));
-		//var_dump($res);
-	}
-
 	function testApiGetSettings(){
 		$res = api_get_settings($cat=null,$ordering='list',$access_url=1,$url_changeable=0);
 		$this->assertTrue(is_array($res));
 		//var_dump($res);
 	}
 
-	function testApiGetSettingsCategories(){
-		$res = api_get_settings_categories($exceptions=array(),$access_url=1);
-		$this->assertTrue(is_array($res));
-		//var_dump($res);
-	}
-
 	function testApiDeleteSetting(){
 		$v = false;
 		$res = api_delete_setting($v, $s=NULL, $a=1);
@@ -579,14 +553,6 @@ class TestMainApi extends UnitTestCase {
 		//var_dump($res);
 	}
 
-/*
-	function testApiDeleteCategorySettings(){
-		$c= false;
-		$res = api_delete_category_settings($c,$a=1);
-		$this->assertTrue(is_bool($res));
-		//var_dump($res);
-	}
-*/
 	function testApiAddSetting(){
 		$sk=null;
 		$type='textfield';
@@ -657,14 +623,6 @@ class TestMainApi extends UnitTestCase {
 		//var_dump($res);
 	}
 
-	function testApiIsInCourse(){
-		$_SESSION['_course']['sysCode']=0;
-		$res=api_is_in_course($course_code=null);
-		$this->assertTrue(is_bool($res));
-		$this->assertTrue(isset($_SESSION['_course']['sysCode']));
-		//var_dump($res);
-	}
-
 	function testApiIsInGroup(){
 		$res=api_is_in_group($group_id=null, $course_code=null);
 		$this->assertTrue(is_bool($res));

+ 14 - 14
whoisonline.php

@@ -16,19 +16,22 @@ if (isset($_GET['cidReq']) && strlen($_GET['cidReq']) > 0) {
 }
 
 $this_section = SECTION_SOCIAL;
-// table definitions
-
-$social_right_content = null;
-$whoisonline_list = null;
+$social_right_content = '';
+$whoisonline_list = '';
 $social_search = '';
 
 // This if statement prevents users accessing the who's online feature when it has been disabled.
 if ((api_get_setting('showonline', 'world') == 'true' && !$_user['user_id']) ||
     ((api_get_setting('showonline', 'users') == 'true' || api_get_setting('showonline', 'course') == 'true') && $_user['user_id'])
 ) {
-
     if (isset($_GET['cidReq']) && strlen($_GET['cidReq']) > 0) {
-        $user_list = who_is_online_in_this_course(0, MAX_ONLINE_USERS, api_get_user_id(), api_get_setting('time_limit_whosonline'), $_GET['cidReq']);
+        $user_list = who_is_online_in_this_course(
+            0,
+            MAX_ONLINE_USERS,
+            api_get_user_id(),
+            api_get_setting('time_limit_whosonline'),
+            $_GET['cidReq']
+        );
     } else {
         $user_list = who_is_online(0, MAX_ONLINE_USERS);
     }
@@ -47,7 +50,6 @@ if ((api_get_setting('showonline', 'world') == 'true' && !$_user['user_id']) ||
 
     $whoisonline_list .= SocialManager::display_user_list($user_list);
 
-
     if (isset($_GET['id'])) {
         if (api_get_setting('allow_social_tool') == 'true' && api_user_is_login()) {
             header("Location: ".api_get_path(WEB_CODE_PATH)."social/profile.php?u=".intval($_GET['id']));
@@ -63,14 +65,12 @@ if ((api_get_setting('showonline', 'world') == 'true' && !$_user['user_id']) ||
 
 $tpl = new Template(get_lang('UsersOnLineList'));
 
-if (api_get_setting('allow_social_tool') == 'true' && !api_is_anonymous()) {
+if (api_get_setting('allow_social_tool') === 'true' && !api_is_anonymous()) {
     $tpl->assign('whoisonline', $whoisonline_list);
     $tpl->assign('social_search', $social_search);
-    $social_layout = $tpl->get_template('social/whoisonline.tpl');
-    $tpl->display($social_layout);
 } else {
-    $content = $social_right_content;
-    $tpl->assign('header', get_lang('UsersOnLineList'));
-    $tpl->assign('content', $content);
-    $tpl->display_one_col_template();
+    $tpl->assign('whoisonline', $social_right_content);
 }
+
+$social_layout = $tpl->get_template('social/whoisonline.tpl');
+$tpl->display($social_layout);