Browse Source

Merge pull request #70 from chamilo/1.9.x

1.9.x
ycastillo 10 years ago
parent
commit
2096ba76b1
100 changed files with 2705 additions and 945 deletions
  1. 3 1
      .gitignore
  2. 2 1
      .travis.yml
  3. 22 22
      custompages/README
  4. 61 50
      custompages/images/footer.png
  5. 6 5
      custompages/language.inc.php
  6. 5 8
      custompages/loggedout.php
  7. 5 9
      custompages/lostpassword.php
  8. 322 10
      custompages/registration-feedback.php
  9. 20 12
      documentation/credits.html
  10. 18 9
      documentation/default.css
  11. 41 26
      documentation/index.html
  12. 10 7
      documentation/installation_guide_es_ES.html
  13. 922 0
      documentation/installation_guide_fr_FR.html
  14. 92 1
      documentation/license.html
  15. 5 2
      documentation/readme.html
  16. 1 0
      license.txt
  17. 3 3
      main/admin/access_url_add_users_to_url.php
  18. 10 10
      main/admin/access_url_check_user_session.php
  19. 5 4
      main/admin/access_url_edit.php
  20. 2 2
      main/admin/access_url_edit_course_category_to_url.php
  21. 7 13
      main/admin/access_url_edit_courses_to_url.php
  22. 2 2
      main/admin/access_url_edit_users_to_url.php
  23. 5 4
      main/admin/access_urls.php
  24. 45 51
      main/admin/add_courses_to_session.php
  25. 1 2
      main/admin/add_courses_to_usergroup.php
  26. 44 43
      main/admin/add_many_session_to_category.php
  27. 1 2
      main/admin/add_sessions_to_promotion.php
  28. 19 24
      main/admin/add_sessions_to_usergroup.php
  29. 0 4
      main/admin/add_users_to_group.php
  30. 25 4
      main/admin/add_users_to_usergroup.php
  31. 2 2
      main/admin/archive_cleanup.php
  32. 3 4
      main/admin/configure_extensions.php
  33. 16 10
      main/admin/configure_homepage.php
  34. 11 10
      main/admin/course_export.php
  35. 3 3
      main/admin/course_import.php
  36. 1 1
      main/admin/course_intro_pdf_import.php
  37. 3 3
      main/admin/course_list.php
  38. 1 0
      main/admin/course_request_accepted.php
  39. 3 3
      main/admin/course_request_rejected.php
  40. 0 1
      main/admin/dashboard_add_courses_to_user.php
  41. 0 1
      main/admin/dashboard_add_users_to_user.php
  42. 22 12
      main/admin/db.php
  43. 1 1
      main/admin/group_list.php
  44. 5 6
      main/admin/inactive_user_list.php
  45. 4 25
      main/admin/languages.php
  46. 33 33
      main/admin/promotions.php
  47. 181 47
      main/admin/resume_session.php
  48. 62 18
      main/admin/session_add.php
  49. 2 2
      main/admin/session_category_add.php
  50. 1 4
      main/admin/session_category_edit.php
  51. 8 17
      main/admin/session_category_list.php
  52. 38 4
      main/admin/session_edit.php
  53. 0 1
      main/admin/session_list.php
  54. 97 0
      main/admin/session_user_edit.php
  55. 2 2
      main/admin/session_user_import.php
  56. 9 8
      main/admin/settings.lib.php
  57. 5 3
      main/admin/settings.php
  58. 1 2
      main/admin/statistics/index.php
  59. 50 3
      main/admin/sub_language.class.php
  60. 1 1
      main/admin/system_announcements.php
  61. 4 4
      main/admin/user_add.php
  62. 55 33
      main/admin/user_edit.php
  63. 1 1
      main/admin/user_export.php
  64. 7 6
      main/admin/user_fields.php
  65. 6 3
      main/admin/user_import.php
  66. 0 5
      main/admin/user_information.php
  67. 2 4
      main/admin/user_move_stats.php
  68. 1 0
      main/admin/usergroup_user_import.php
  69. 1 5
      main/admin/usergroups.php
  70. 1 1
      main/announcements/announcements.inc.php
  71. 11 12
      main/announcements/announcements.php
  72. 0 3
      main/attendance/attendance_controller.php
  73. 1 1
      main/auth/cas/lib/CAS/client.php
  74. 108 30
      main/auth/inscription.php
  75. 21 16
      main/auth/lostPassword.php
  76. 7 7
      main/auth/my_progress.php
  77. 0 1
      main/auth/profile.php
  78. 56 25
      main/auth/set_temp_password.php
  79. 31 60
      main/blog/blog.php
  80. 3 3
      main/blog/blog_admin.php
  81. 7 10
      main/calendar/agenda.inc.php
  82. 17 5
      main/calendar/agenda.lib.php
  83. 1 11
      main/calendar/allagendas.php
  84. 4 6
      main/calendar/ical_export.php
  85. 2 15
      main/calendar/myagenda.inc.php
  86. 1 1
      main/chat/chat_chat.php
  87. 1 1
      main/chat/chat_hidden.php
  88. 11 18
      main/chat/chat_message.php
  89. 21 13
      main/chat/chat_whoisonline.php
  90. 3 3
      main/chat/footer_frame.inc.php
  91. 41 79
      main/chat/header_frame.inc.php
  92. BIN
      main/chat/sound/notification.mp3
  93. BIN
      main/chat/sound/notification.ogg
  94. BIN
      main/chat/sound/notification.wav
  95. BIN
      main/chat/sound/sonido_notificacion.mp3
  96. BIN
      main/chat/sound/sonido_notificacion.ogg
  97. BIN
      main/chat/sound/sonido_notificacion.wav
  98. 1 0
      main/course_description/add.php
  99. 7 2
      main/course_description/course_description_controller.php
  100. 3 3
      main/course_home/3column.php

+ 3 - 1
.gitignore

@@ -32,4 +32,6 @@ searchdb/*
 .idea/cssxfire.xml
 
 *.orig
-nbproject/*
+nbproject/*
+
+plugin/bbb/config.vm.php

+ 2 - 1
.travis.yml

@@ -1,6 +1,7 @@
 {
     "require": {
         "php-ffmpeg/php-ffmpeg": "0.3.x-dev@dev",
-        "sabre/vobject" : "~3.1"
+        "sabre/vobject": "~3.1",
+        "toin0u/digitalocean": "~1.4"
     }
 }

+ 22 - 22
custompages/README

@@ -16,29 +16,29 @@ require_once (api_get_path(LIBRARY_PATH).'usermanager.lib.php');
 if (! isset($_SESSION['conditional_login']['uid']))
   die("Not Authorised");
 
-if (isset($_POST['password'])){
-  $u = UserManager::get_user_info_by_id($_SESSION['conditional_login']['uid']);
-  if ($_POST['password'] != $_POST['password2']) { 
-    header('Location: '. api_get_self().'?invalid=2');
-    exit();
-  }
-  if (empty($_POST['password'])){ //|| !api_check_password($password)) { //Pass must be at least 5 char long with 2 digits and 3 letters
-    header('Location: '. api_get_self().'?invalid=1');
-    exit();
-  }
-  $password = $_POST['password'];
-  $updated = UserManager::update_user($u['user_id'], $u['firstname'], $u['lastname'], $u['username'], $password, $u['auth_source'], $u['email'], $u['status'], $u['official_code'], $u['phone'], $u['picture_uri'], $u['expiration_date'], $u['active'], $u['creator_id'], $u['hr_dept_id'], null, $u['language'],'');
+if (isset($_POST['password'])) {
+    $u = UserManager::get_user_info_by_id($_SESSION['conditional_login']['uid']);
+    if ($_POST['password'] != $_POST['password2']) {
+        header('Location: '. api_get_self().'?invalid=2');
+        exit();
+    }
+    if (empty($_POST['password'])){ //|| !api_check_password($password)) { //Pass must be at least 5 char long with 2 digits and 3 letters
+        header('Location: '. api_get_self().'?invalid=1');
+        exit();
+    }
+    $password = $_POST['password'];
+    $updated = UserManager::update_user($u['user_id'], $u['firstname'], $u['lastname'], $u['username'], $password, $u['auth_source'], $u['email'], $u['status'], $u['official_code'], $u['phone'], $u['picture_uri'], $u['expiration_date'], $u['active'], $u['creator_id'], $u['hr_dept_id'], null, $u['language'],'');
 
-  if ($updated) {
-    UserManager::update_extra_field_value($u['user_id'], 'already_logged_in', 'true');
-    ConditionalLogin::login();
-  }
+    if ($updated) {
+        UserManager::update_extra_field_value($u['user_id'], 'already_logged_in', 'true');
+        ConditionalLogin::login();
+    }
 }
 if ($_GET['invalid'] == 1) {
-  $error_message = get_lang('CurrentPasswordEmptyOrIncorrect');
+    $error_message = get_lang('CurrentPasswordEmptyOrIncorrect');
 }
 if ($_GET['invalid'] == 2) {
-  $error_message = get_lang('PassTwo');
+    $error_message = get_lang('PassTwo');
 }
 /**
  * HTML output
@@ -59,11 +59,11 @@ if ($_GET['invalid'] == 2) {
 	<script type="text/javascript" src="/main/inc/lib/javascript/jquery.min.js"></script>
 	<script type="text/javascript">
 		$(document).ready(function() {
-			if (top.location != location) 
+			if (top.location != location)
 				top.location.href = document.location.href ;
 
 			// Handler pour la touche retour
-			$('input').keyup(function(e) { 
+			$('input').keyup(function(e) {
 				if (e.keyCode == 13) {
 					$('#changepassword-form').submit();
 				}
@@ -80,12 +80,12 @@ if ($_GET['invalid'] == 2) {
 			<img src="/custompages/images/header.png" alt="Logo" />
 		</div> <!-- #header -->
     <h2> <?php echo custompages_get_lang('FirstLogin');?> </h2>
-        
+
 		<div id="changepassword-form-box" class="form-box">
       <div class="info"> <?php echo custompages_get_lang('FirstLoginChangePassword');?> </div>
 		<?php if (isset($error_message)) {
 			echo '<div id="changepassword-form-error" class="form-error">'.$error_message.'</div>';
-		}?> 
+		}?>
 			<form id="changepassword-form" class="form" method="post">
 				<div>
           <label for="password">*<?php echo custompages_get_lang('langPass');?></label>

+ 61 - 50
custompages/images/footer.png


+ 6 - 5
custompages/language.inc.php

@@ -1,7 +1,7 @@
 <?php
 /* For licensing terms, see /license.txt */
 /**
- * Definition of language-related functions for cases where th user isn't 
+ * Definition of language-related functions for cases where th user isn't
  * logged in yet
  * @package chamilo.custompages
  */
@@ -35,11 +35,11 @@ function custompages_get_lang($variable) {
 	return get_lang($variable, null, $_SESSION['user_language_choice']);
 }
 
-$language_file = array('courses', 'index', 'registration', 'admin','userInfo');
-$available_langs = array('en','fr');
+$language_file = array('courses', 'index', 'registration', 'admin', 'userInfo');
+$available_langs = array('en', 'fr', 'es');
 $chamilo_langs = array(null => 'english', 'en' => 'english', 'fr' => 'french', 'nl' => 'dutch', 'de' => 'german', 'es' => 'spanish');
 $lang_match = $chamilo_langs[get_preferred_language($available_langs)];
-// recover previous value ... 
+// recover previous value ...
 if (isset($_SESSION['user_language_choice']))
 	$lang_match = $_SESSION['user_language_choice'];
 
@@ -52,5 +52,6 @@ if (isset($_REQUEST['lang']) && !empty($_REQUEST['lang']) && in_array($_REQUEST[
 	$lang_match = $chamilo_langs[$_REQUEST['lang']];
 }
 $_user['language'] = $lang_match;
+
 $_SESSION['user_language_choice'] = $lang_match;
-?>
+

+ 5 - 8
custompages/loggedout.php

@@ -8,12 +8,11 @@
  * Init
  */
 $called_direcly = !function_exists('api_get_path');
-if ($called_direcly)
-{
+if ($called_direcly) {
     return '';
 }
 
-require_once('language.php');
+require_once 'language.php';
 $www = api_get_path('WEB_PATH');
 
 /**
@@ -31,12 +30,10 @@ $www = api_get_path('WEB_PATH');
         <!--[if IE 6]>
         <link rel="stylesheet" type="text/css" href="/custompages/style-ie6.css" />
         <![endif]-->
-
         <script type="text/javascript" src="<?php echo $www ?>main/inc/lib/javascript/jquery.min.js"></script>
-
         <script type="text/javascript">
             $(document).ready(function() {
-                if (top.location != location) 
+                if (top.location != location)
                     top.location.href = document.location.href ;
             });
         </script>
@@ -58,7 +55,7 @@ $www = api_get_path('WEB_PATH');
             <a href="<?php echo $www . 'user_portal.php'; ?>">Go to your portal</a>
             <div id="footer">
                 <img src="<?php echo $www ?>/custompages/images/footer.png" alt="footer"/>
-            </div> 
-        </div> 
+            </div>
+        </div>
     </body>
 </html>

+ 5 - 9
custompages/lostpassword.php

@@ -3,19 +3,15 @@
 /**
  * Quick form to ask for password reminder.
  * @package chamilo.custompages
- */ 
-/**
- * Initialization
  */
-require_once('../../main/inc/global.inc.php'); 
+
+require_once('../../main/inc/global.inc.php');
 require_once('language.php');
-/**
- * HTML
- */
+
 ?>
 <html>
 <head>
-	<title>Password recovery</title>
+	<title><?php echo custompages_get_lang('LostPassword');?></title>
 	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 	<!--[if !IE 6]><!-->
 	<link rel="stylesheet" type="text/css" href="../../custompages/style.css" />
@@ -27,7 +23,7 @@ require_once('language.php');
 	<script type="text/javascript">
 		$(document).ready(function() {
 			// Handler pour la touche retour
-			$('input').keyup(function(e) { 
+			$('input').keyup(function(e) {
 				if (e.keyCode == 13) {
 					$('#lostpassword-form').submit();
 				}

+ 322 - 10
custompages/registration-feedback.php

@@ -44,7 +44,55 @@
 <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>
 
-<h1>Chamilo 1.9.8 - Mystery name, Mystery date, 2013</h1>
+<h1>Chamilo 1.9.8.3 - 21st of July, 2014</h1>
+<h3>Release notes - summary</h3>
+<p>Chamilo 1.9.8.3 is a small patch version with a few minor bugfixes and a new packaging. This will be packaged and promoted as 1.9.8, but the folder inside the 1.9.8 will be called 1.9.8.3, with a few changes to this changelog file and a few fixes throughout the code (as such, you can just overwrite previous files to upgrade from 1.9.8, 1.9.8.1 or 1.9.8.2 to 1.9.8.3).</p>
+<h3>Release name</h3>
+<p><a href="http://es.wikipedia.org/wiki/Departamento_de_Huánuco">Huánuco</a> is a small city in the Peruvian Andes, Northeast of Lima. This is a special version in memory of our cherished development team member César Perales, who passed away on July 22nd, 2014, at age 27. César contributed mostly "in the shadow" to Chamilo LMS, allowing the rest of the team to contribute more actively. He was a vibrant young man. He will be missed. César lived in calle Huánuco, in La Molina, Lima, Peru, where other team members bid him their last farewell for his last, eternal trip.</p>
+
+<h3>New Features</h3>
+<ul>
+  <li>(eadeaf8 - BT#8317) Add sessions duration feature</li>
+  <li>(019987d - BT#8316) Allow courses to be sorted inside a session (requires manual database change)</li>
+</ul>
+
+<h3>Improvements (minor features)</h3>
+<ul>
+  <li>(4d15a0f - BT#8417) Fix hide buttons in users report<li>
+  <li>(5213de2) Fix 1800 seconds flaw in learning paths availability</li>
+  <li>(5660b37) Fix download link in install doc</li>
+  <li>(09dc3f3) Update install doc for full-text indexing</li>
+  <li>(333319b) Fix deadlock condition in documents download</li>
+  <li>(bc94d5c - BT#8392) Allow ExtraFieldValue method to get the last value instead of the first in case of duplicates</li>
+  <li>(bbe627b - BT#8268) Add sso_challenge method as a second alternative to SSO authentication</li>
+  <li>(5ed701e) Add optimization documentation for user_rel_tag table index</li>
+  <li>(1204e13) Fix who is online user preview</li>
+  <li>(a98ab8c - BT#8157) Fix error loading wav files using LP</li>
+  <li>(cfac025 - BT#7780) Fix QTI import response</li>
+  <li>(c830878 - BT#7780, BT#6819) Fix condition showing deleted exercises</li>
+  <li>(c98d190 - BT#7803) Show events for "everyone" when showing user events</li>
+  <li>(f01b3b5 - BT#7780) Fix Aiken exercise import redirect display</li>
+  <li>(595fafb - BT#8231) Add setting to decode UTF-8 in registration web services</li>
+  <li>(39f5916 - BT#8295) Add Spanish to available languages in custom pages</li>
+  <li>(3995283 - BT#8295) Fix lost password page in custom pages</li>
+  <li>(528102f - BT#8289) Fix Chrome error when loading page HTML source</li>
+  <li>(9c0c73a - BT#8157) Fix issue in documents edition in multiple open courses context</li>
+  <li>(fbda6e2 - BT#8157) Add export users-class to CSV</li>
+  <li>(fd61c0f) Add missing icons</li>
+  <li>(a55776c - BT#8189) Fix for users inscription with approval setting</li>
+  <li></li>
+</ul>
+
+
+<h1>Chamilo 1.9.8.2 - 21st of June, 2014</h1>
+<h3>Release notes - summary</h3>
+<p>Chamilo 1.9.8.2 is a very little patch version with one bugfix regarding the learning paths tool. Considering the fact that 1.9.8 is planned for the long term, we'd hate to have such a minor patch left on the side for a year or so. This will be packaged and promoted as 1.9.8, but the folder inside the 1.9.8 will be called 1.9.8.2, with a change to this changelog file and a one-line change to main/newscorm/learnpathItem.class.php (as such, you can update just this file to upgrade from 1.9.8.1 to 1.9.8.2. See <a href="https://github.com/chamilo/chamilo-lms/commit/642a80e9e838f98f25135fbb64795e4f3df86260">the code change</a> for detais.</p>
+
+<h1>Chamilo 1.9.8.1 - 18th of June, 2014</h1>
+<h3>Release notes - summary</h3>
+<p>Chamilo 1.9.8.1 is a patch version with one security patch on top of 1.9.8 (in the included library for FCKeditor). Please check <a href="https://support.chamilo.org/projects/chamilo-18/wiki/Security_issues">our security issues page</a> for more information.</p>
+
+<h1>Chamilo 1.9.8 - Thon, 15th of June, 2014</h1>
 <h3>Release notes - summary</h3>
 <p>Chamilo 1.9.8 is a minor stable version with a series of improvements on top of 1.9.6.<br />
 This version is the first Chamilo version to drop support for Internet Explorer
@@ -53,10 +101,12 @@ web standards. If they *cannot* avoid Internet Explorer, make sure they use at
 least version 10, which respects a little bit more than half of the W3C
 standards (but still much less than Firefox, Chrome, Opera or even Safari)</p>
 <h3>Release name</h3>
-<p><a href="http://fr.wikipedia.org/wiki/">City name</a> is a city ... </p>
+<p><a href="http://fr.wikipedia.org/wiki/Thon-Samson">Thon</a> is a small city in the Belgian region of Wallonia, several times classified as the most beautiful village of the South region of Belgium. It is a quite, beautiful place without anything out of the ordinary but made of beautiful, hundred years old homes built from famous Wallonia blue stone and crossed by the Samson river. Its stability and it's position just next to the large cliffs surrounding the Meuse are symbolically close to Chamilo 1.9.8, highly stable but a few steps away for the huge jump to the next major version.</p>
 <h3>Security</h3>
+All security issues are published and patches are attached on <a href="https://support.chamilo.org/projects/chamilo-18/wiki/Security_issues">our security issues page</a>. If you think you found an additional security issue you'd like to report, please check our procedure there.
 <ul>
-  <li></li>
+  <li>Patches have been applied to one of the packages of FCKEditor used for image uploads in Chamilo 1.9.6.1. These patches are included in 1.9.8 (see issues 11 and 12)</li>
+  <li>Some other possible XSS attack vectors through initially privileged access have been fixed (see issue #13)</li>
 </ul>
 <h3>New Features</h3>
 <ul>
@@ -67,7 +117,6 @@ standards (but still much less than Firefox, Chrome, Opera or even Safari)</p>
     <li>(#6165) Added option to prevent "login as" feature on enhanced-security portals</li>
     <li>(#6213) Register "user disabling" action in important activities</li>
     <li>(#6216) Make check_version() AJAX-based</li>
-    <li>(#6348 - ongoing) Added possibility to add individual tasks to learning paths</li>
     <li>(#6373) Added CAPTCHA on registration page (requires manual configuration edition)</li>
     <li>(#6416) Added "Sessions subscribed to" icon in users list</li>
     <li>(#6513) Exercises: Added auto-evaluation mode with feedback but without correct answer hint</li>
@@ -82,6 +131,9 @@ standards (but still much less than Firefox, Chrome, Opera or even Safari)</p>
     <li>(#7056) Exercises: Added auditing of "clean results" action by teachers</li>
     <li>(#7063) Added support for "for" attributes in &lt;label&gt; fields for WCAG/WAI AA compatibility</li>
     <li>(#7072) Added user profile fields of type "File upload"</li>
+    <li>(BT#7010) Add Aiken (Word) import format in exercises</li>
+    <li>(#6224) Add browser language auto-detection at first connection</li>
+    <li>(#5464) Added BuyCourses plugin for PayPal payments</li>
 </ul>
 <h3>Improvements (minor features)</h3>
 <ul>
@@ -112,13 +164,19 @@ standards (but still much less than Firefox, Chrome, Opera or even Safari)</p>
     <li>(#6836) Added select_ajax element to FormValidator (internal stuff)</li>
     <li>(#6882) Courses catalog: Now showing courses that were not in any category</li>
     <li>(#6889) Exercises: Group feedback and results options in exercises creation/edition form</li>
+    <li>(#7158) Increase recommended max memory limit to 128MB to avoid problems with reports with lots of users</li>
+    <li>(#7161) Thematic plan's edition mode doesn't display properly</li>
+    <li>(#6768) Always redirect user to expected page after "Not allowed" message (always load the require_uri in api_not_allowed) and fix login button</li>
+    <li>(bd0320a,d2ddee1 - BT#8199) Add session support in exams report</li>
+    <li>(#6173) Add partial French translation to installation guide</li>
+    <li>(#7192) Add possibility to download student assignment document from grading form</li>
 </ul>
 <h3>Debugging</h3>
 <ul>
-    <li>(#398)  Learning paths: Fixed issue with SCORM Scenari courses which did not set status to completed when expected</li>
     <li>(#4067) Groups: Student cannot see group documents on first access</li>
     <li>(#4788) Documents: Nanogong files do not play in document tool</li>
     <li>(#5751) Exercises: Quotes were forbidden/filtered in "question" field</li>
+    <li>(#----) Fix CSV import to allow default export format</li>
     <li>(#5755) Documents: Audio creation with Pediaphon was broken</li>
     <li>(#5878) Exercises: Validation bug for answers</li>
     <li>(#5881) Exercises: Link when opened question was answered opened a blank page</li>
@@ -183,10 +241,10 @@ standards (but still much less than Firefox, Chrome, Opera or even Safari)</p>
     <li>(#6708) User cannot move to next question</li>
     <li>(#6724) Group forum not added in forum tool if forum category doesn't exist</li>
     <li>(#6728) YesNo survey icon show checkbox instead of radio</li>
-    <li>(#6735) Escape MySQL wildcards characters in admin > user list advanced search form</li>
+    <li>(#6735) Escape MySQL wildcards characters in admin &gt; user list advanced search form</li>
     <li>(#6741) Create course link in admin panel is removed when course suggestion enabled</li>
     <li>(#6755) Search in session screen doesn't work</li>
-    <li>(#6761) Admin > Create new user > "Is admin" should be hidden by default</li>
+    <li>(#6761) Admin &gt; Create new user &gt; "Is admin" should be hidden by default</li>
     <li>(#6764) Weird multi-url splitting condition bug when using multiple server_name in Nginx</li>
     <li>(#6772) Hotspot: not registering first area (related to #6310)</li>
     <li>(#6773) Long-standing sub-language bug: terms saving sometimes misses "</li>
@@ -206,7 +264,7 @@ standards (but still much less than Firefox, Chrome, Opera or even Safari)</p>
     <li>(#6827) SQL error when backuping a course</li>
     <li>(#6828) Question categories are not restored from course backup</li>
     <li>(#6829) Anonymous Surveys doesn't work</li>
-    <li>(#6831) Copy & paste from Word gives some errors</li>
+    <li>(#6831) Copy &amp; paste from Word gives some errors</li>
     <li>(#6835) Constant TOOL_AGENDA does not exists</li>
     <li>(#6842) Forum messages issues</li>
     <li>(#6845) Problem submitting work to legacy deleted folders</li>
@@ -274,22 +332,276 @@ standards (but still much less than Firefox, Chrome, Opera or even Safari)</p>
     <li>(#7073) Session coach does not see session</li>
     <li>(#7076) Chozen languages bar too large in IE11 and Safari 7</li>
     <li>(#7077) Learning path - Didn't show the bottom part of large documents</li>
+    <li>(#7111) Blank screen when accessing public learning path as anonymous user with Chrome</li>
+    <li>(#7112) Recycle course doesn't delete all documents</li>
+    <li>(#7131) Time issues with total time connected when switching courses in a non-0 timezone</li>
+    <li>(#7137) Course catalog - "Display all" mode only shows one course</li>
+    <li>(#7149) DatePicker class not found on Windows</li>
+    <li>(#7153) Wrong gradebook results in sessions with gradebook categories</li>
+    <li>(#7155) Exercise description JS bug inside exercise view</li>
+    <li>(#7162) Date not editable in announcements</li>
+    <li>(#7171) Impossible to preview exercise with unlimited attempts more than once</li>
+    <li>(BT#6294) Works: Fix insert_user_id for uploads + added cidReq in work forms to avoid loosing reference</li>
+    <li>(BT#6223) Add missing "group by" in SQL clause</li>
+    <li>(BT#6329) Fix many notice messages</li>
+    <li>(BT#5928) Do not set sections as prequerisites in learning paths</li>
+    <li>(31984a1) Add Euskera translation</li>
+    <li>(c3a1b4a) Update Chamilo Rapid setup procedure in installation guide</li>
+    <li>(baba938) Register survey answers with neutral (anonymous) link</li>
+    <li>(b837405 - BT#6369) Add CSV import through CRON capability</li>
+    <li>(bc13344) SCORM: Update scorm_api to manage progress and time on documents inserted inside a SCORM LP</li>
+    <li>(405f950 - BT#6369) Integrate Monolog</li>
+    <li>(69f3809 - BT#6369) Grouping of notification messages into one single e-mail</li>
+    <li>(b7034b0 - BT#6451) Add sessions list icon to admin's users list</li>
+    <li>(18cda53 - BT#6451) Remove useless spacing in icon groups in admin's users list</li>
+    <li>(3f178c6 - BT#6487) Add jQuery password strength meter</li>
+    <li>(c2cf9c2 - BT#6487) Add password strength filter</li>
+    <li>(37597b8 - BT#6486) Add CAPTCHA after failed login attempts - see configuration.dist.php for config template</li>
+    <li>(e9a0925 - BT#6486) loginFailed check now based on session (instead of $_GET)</li>
+    <li>(fc452e9 - BT#6369) Add dump all and extra fields to cron CSV import</li>
+    <li>(21021fb) Change wordwrap param to 120 in jQGrid</li>
+    <li>(3cfed54) Fix document templates not appearing in learning path edition page</li>
+    <li>(ec82b74 - BT#6449) Add new params when creating a session</li>
+    <li>(b13415c - BT#6369) Add configuration setting for default import admin user in cron CSV import</li>
+    <li>(51b6982 - BT#6418) Add new UI for works tool</li>
+    <li>(16af474 - BT#6418) Fix bug in expiry time check in works tool</li>
+    <li>(e29a326 - BT#6418) Add mising works + reminder options in works tool</li>
+    <li>(d0f01d6 - BT#6537) Add Vimeo embed code for LPs</li>
+    <li>(af9a4ad - BT#6509) Fix file element size in form</li>
+    <li>(75f7f7b - BT#6509) Remove input fields decoration inside questions</li>
+    <li>(4e01256 - BT#6536) Force download for DWG and DWF files</li>
+    <li>(03b46d3) Update jQGrid version to 4.5.2</li>
+    <li>(33c43a1 - BT#6369) Force session list for user when looking as an admin</li>
+    <li>(f26a010 - BT#6369) Admin can see the relations between user and sessions</li>
+    <li>(38b2a1e) Remove possibility to download invisible works</li>
+    <li>(3b5b267) Only teachers can edit works</li>
+    <li>(237dd03) Fix upload and add view icon for students in works tool</li>
+    <li>(48f7d5d - BT#6560) Fix bug in MATCHING question type when using shuffled answers</li>
+    <li>(48fc623 - BT#5854) Fix orphan questions bug</li>
+    <li>(a941044 - BT#6509) Calculate fill-blanks input width on the fly</li>
+    <li>(3592e1b, 02751d5) Fix learnpath_item_id param in SOAP web service for learning path</li>
+    <li>(a4b86fc - BT#5848) Reading the Username, username, Id or id rows when importing users to a course</li>
+    <li>(7c1cb6f - BT#6418) Fix local dates issues in works tool</li>
+    <li>(8d0c9da) Fix right-floating footer in all Chamilo styles</li>
+    <li>(7fcbd56, 24495c9) Implementation of "hidden" visibility for courses</li>
+    <li>(c000a0e - BT#6619) Add studentview support in api_is_coach()</li>
+    <li>(5f4c961 - BT#6613) Testing addition of audio recording through WebRTC (later disabled)</li>
+    <li>(8f4f547 - BT#6613) Add mediaelement JS library to reproduce audio and video tags</li>
+    <li>(b0e020c - BT#6614, BT#6615) Add "Add document" and "Add user" to new works tool UI</li>
+    <li>(37f6995 - BT#6615) Block works list access for students not subscribed</li>
+    <li>(fbc9eaa - BT#6449) Add fields exclusion in cron CSV import</li>
+    <li>(2ab57d4 - BT#6616) Add document/works templates in works tool</li>
+    <li>(8213304, 954c8f1) Fix FCKEditor + IE10 bug SCRIPT438</li>
+    <li>(db03389) Fix IE8 issue when loading LP audio</li>
+    <li>(37d724f - BT#6660) Fix icon in users folder (documents tool)</li>
+    <li>(3112a12 - BT#6666) Fix category code on course creation</li>
+    <li>(85a1d62 - BT#6675) Fix bug in AJAX filemanager search</li>
+    <li>(ea749fa - BT#6675) User must login to search a document</li>
+    <li>(fb51cb0) Add trim when looking for usernames</li>
+    <li>(a475e63 - BT#6449) Allow multiple teachers in a course on CSV import</li>
+    <li>(b792956 - BT#6692) Fix jQGrid count when searching for sessions + fix category URL</li>
+    <li>(c0c4e1e) Add access_url_id in sessions category queries</li>
+    <li>(5c29dd4 - BT#6695) Add a global default_calendar_view setting in the agenda</li>
+    <li>(6145ecb - BT#6708) Add pagination when searching users in the social network</li>
+    <li>(5f63106 - BT#6708) Fix user search query</li>
+    <li>(8f5b7e0) Allow session manager to see session (query fix)</li>
+    <li>(105e30e) Add SCORM logs colouring in JS console</li>
+    <li>(c6b00d7) Fix missing real_id and point_info keys, while searching the catalog</li>
+    <li>(9f5e804) Fix password protected course</li>
+    <li>(dde291e) Fix coaches assignation when updating the course session using course_edit.php</li>
+    <li>(45ee32f - BT#6692) If a coach user is deleted, the admin is assigned as coach and session_admin_id</li>
+    <li>(9e045b8 - BT#6524, #6708) Comment code that deletes the attempt of the user</li>
+    <li>(c08cbc0 - BT#6722) Fix class import</li>
+    <li>(4c7a861 - BT#6682) Adding setVisible/setInvisible actions in the document list</li>
+    <li>(4c7a861) Adding user_name_order setting does not work in 1.9 + fixing the user list order</li>
+    <li>(82914a0) Add new Drupal SSO counterpart</li>
+    <li>(fe018b5 - BT#6418) Work authors can *always* see their work</li>
+    <li>(fd502ac) Fix for IP address tracking</li>
+    <li>(67c2748 - BT#6532) Hiding certificate URL</li>
+    <li>(5b8c384) If sender info is not set then try to use: 1. the no-reply email 2. the admin email</li>
+    <li>(a425a23 - BT#6418) User can edit work if the "delete own document" is on and the work has not been reviewed</li>
+    <li>(a035d0f) Allow modification of max number of user in groups</li>
+    <li>(f658496) When editing a category group all settings are copied to the groups</li>
+    <li>(dce1900 - BT#6738) Fixed 'shared_survey' mysql error during migration from 1.6.x to 1.8.x</li>
+    <li>(480eb84) drh can access all content (courses, users) inside a session - see config template in configuration.dist.php</li>
+    <li>(b81625b - BT#6687) Hide session if it doesn't have any content (courses)</li>
+    <li>(efdc138) WARNING - This is a hack to the database structure, which will be ignored if not present: Add "c_student_publication.filename" field in order to separate the changes between student and teacher</li>
+    <li>(cc1dfbb) WARNING - This is a hack to the database structure, which will be ignored if it isn't present: Add new field in the DB course.add_teachers_to_sessions_courses (ignored if field doesn't exist)</li>
+    <li>(2de4c45 - BT#6449) Changing course_edit.php update teachers</li>
+    <li>(b044222 - BT#6776) Adding an event_system() notification when personal documents are deleted</li>
+    <li>(c1d3e10 - BT#6799) left_width value is now generated at runtime via jquery</li>
+    <li>(c623215) Add coments per work, see template config in configuration.dist.php</li>
+    <li>(2e7a124) Fix install issue preventing the right check on directories creation</li>
+    <li>(3f23c7f) Teachers can be removed in the course_edit.php page</li>
+    <li>(599734e - BT#6449) Add course coaches addition feature on admin/course_edit.php page</li>
+    <li>(df5ea90) Add new settings documents_default_visibility_defined_in_course/documents_default_visibility...</li>
+    <li>(90f6055 - BT#6815) Add course id when showing items</li>
+    <li>(b00b6da - BT#6734) Add agenda events list</li>
+    <li>(ed9df81) Fix LDAP code</li>
+    <li>(dbdc8cc - BT#6621) When hiding a document in a course it also hides the document in all sessions</li>
+    <li>(804ef62 - BT#6848) Fix session history link</li>
+    <li>(cfef1d8 - BT#6801) Fix SQL error when getting orphan questions</li>
+    <li>(9985547 - BT#6845) Add unsubscribe user from session/course option</li>
+    <li>(2bc0785 - BT#6770) Add "login as" button for admins in course tracking</li>
+    <li>(fcd6e26) Teacher don't need the show_email_addresses option to see phone/email when exporting to PDF</li>
+    <li>(9d2fc90 - BT#6770) DRH can "login as" users</li>
+    <li>(9533f3b) Adding icon + fixing course count in tracking pages</li>
+    <li>(428a2b2) Admin can access the users' stats</li>
+    <li>(4ee3a13 - BT#6770) Add user list for DRH with search box in tracking pages</li>
+    <li>(5cfe24f) WARNING - This is a hack to the database structure, which will be ignored if not present: Add multiple URL support for classes (user group) new DB table added "access_url_rel_user_class" - only available on fresh installs, on manual DB update or by waiting for v10</li>
+    <li>(9063249 - BT#6845) Add course session coaches in the get_personal_session_course_list()</li>
+    <li>(1f162d6 - BT#6195) Add support for multiple URL for course categories</li>
+    <li>(09a2e8a - BT#6782) Add teachers to course from a session</li>
+    <li>(4bc737b - BT#6782) Change session source to multiple option</li>
+    <li>(bc15484 - BT#6962) Add course code in URL in order to fix the "newscorm -1" error</li>
+    <li>(e84ef79) Add handy function api_get_plugin_setting(), adding new "main_top" and "main_bottom"</li>
+    <li>(05db5ec - BT#6939) Add language support in the before login plugin (other commits improve the plugin in other areas)</li>
+    <li>(d13198c - BT#6963) Fix jQGrid ordering by column</li>
+    <li>(cfa3ee4) Fix issue whereby platform admins are not considered course admins</li>
+    <li>(f84285b) Fix youtube fckeditor plugin : use protocol relative url</li>
+    <li>(ed93152) Implement new work student adding score/status in student view</li>
+    <li>(6d3a26b - BT#6977) Add $_configuration['allow_my_files_link_in_homepage']</li>
+    <li>(6e572a9) Fix flashvars in learnpath : replace only relative urls</li>
+    <li>(8e8ab98 - BT#6985) Group tool: Adding import/export categories and groups CSV</li>
+    <li>(5a696e6 - BT#6985) Add search form, tutors and members list in group overview</li>
+    <li>(f227d91 - BT#6985) Add PDF export to group overview page</li>
+    <li>(6c2506c - BT#6770) Do not edit DRH relationships when importing by CSV</li>
+    <li>(979e4f5 - BT#7041) Only show courses from the current url</li>
+    <li>(80a519f - BT#7027) When removing a user from a course the works are deleted too</li>
+    <li>(21ed2a1 - BT#7007) Implemented webservice-based authentication</li>
+    <li>(726eba9 - BT#6916) Move extra DB changes in an extra.sql custom file</li>
+    <li>(fde063a - BT#6700) Fix IE10 bug in messages</li>
+    <li>(f1a474f - BT#7008) Hiding Home_top when connected and hide global announcements when not connected - special options hide_global_announcements_when_not_connected and hide_home_top_when_connected, see config template in configuration.dist.php</li>
+    <li>(1b939ab - BT#7007) Added phpseclib library to allow for AES128/CFB encryption mode</li>
+    <li>(3c21fdb - BT#6613) Add php-ffmpeg library</li>
+    <li>(c768066 - BT#7010) Add special options for Aiken format reading in non-UTF8</li>
+    <li>(2a3c26b - BT#7008) Allow public/private tab links in navigation menu</li>
+    <li>(d667207 - BT#7064) Added use of default course template parameter (course_creation_use_template) - see template config in configuration.dist.php</li>
+    <li>(870673c - BT#7011) Add sessions filter to courses list</li>
+    <li>(ba45725) Fix unzip_uploaded_file()</li>
+    <li>(5bd2e52 - BT#7066) Add script to massively upload one file into each course's documents and link from course introduction - needs to be enabled in main/admin/index.php</li>
+    <li>(be2304e - BT#7070) Fix default documents listing order</li>
+    <li>(fa868fb - BT#7011) Add courses filter to sessions list</li>
+    <li>(e9035f5 - BT#7089) Add support for DRH in api_is_platform_admin()</li>
+    <li>(a937194 - BT#7013) Add user access by session report</li>
+    <li>(c8e3425 - BT#7015) Add user learning path progress by session report</li>
+    <li>(86d47af - BT#7089) Add link to "company report" in admin page for DRH</li>
+    <li>(ebc2324) Add support for test categories in course backup/restore</li>
+    <li>(138e5e4 - BT#6642) Fix error when deleting documents</li>
+    <li>(cc5b2ec - BT#6770) Allow DRH to enter course as student, see config template in configuration.dist.php</li>
+    <li>(a721c2b - BT#6770) Add courses and sessions filter on active status</li>
+    <li>(7640e68 - BT#7016) Log user access to wiki</li>
+    <li>(4052f7e - BT#7017) Add exercise progress report</li>
+    <li>(092a136) Add permissions check in dropbox</li>
+    <li>(48d356f) Add sessions count to data reporting from Chamilo</li>
+    <li>(4f0a911) Fix text escaping issues in exercises titles</li>
+    <li>(d07dbf1 - BT#7110) Course introduction is now the default for sessions, then any edition in session makes a session-level copy</li>
+    <li>(62ebad5 - BT#7116) Session import fixes allowing multiple users in all sessions</li>
+    <li>(84c261c - BT#7123) Add tracking info about who created the user in user edition page</li>
+    <li>(65e26c4) Fix bug blocking JS from executing under HTTPS</li>
+    <li>(311e823 - BT#6770) Add sleeping teachers/students counter in reporting tab</li>
+    <li>(05a26a4 - BT#7119) Disabling messages/emails if user is inactive</li>
+    <li>(c75cc0b - BT#7017) Add CSV export to admin reports</li>
+    <li>(a39abd0) Don't unregister teacher course when adding user in course with csv import</li>
+    <li>(69bc79f - BT#7128) Change delete document mechanism to use doc ID instead of path</li>
+    <li>(030315b) Fix access to document folders inside sessions: Add first check on session doc, then check base course</li>
+    <li>(703b1be) Redirect DRH directly to sessions (paginated list) to avoid loading the very expensive index</li>
+    <li>(3eda877 - BT#7140) Add link to course from session tracking detailed list</li>
+    <li>(b159706 - BT#7142) Add code to course search possibilities</li>
+    <li>(e4bc5ed - BT#7140) Add condition to avoid high-load useless session queries</li>
+    <li>(a0901d0 - BT#7156) Groups changes: add event_system() call to log stuff from groups, add 'allow_members_leave_group' option. WARNING: this requires a database change which is ignored if it doesn't exist</li>
+    <li>(d2514af - BT#7163) Add session_import_drh.php script</li>
+    <li>(9a73828 - BT#7174) Allow exercise edition even if it's added in a LP, "dangerous" fields are frozen</li>
+    <li>(59d48b7) Change REL_COURSE PATH by WEB_COURSE_PATH when it convert a mp3 url to absolute</li>
+    <li>(24565f3 - BT#7217) Add addAnnouncementToAllUsersInSessions option</li>
+    <li>(a27ce6b - BT#7217) Add session ID to link in announcement sent from session</li>
+    <li>(4e0f217 - BT#7104) Add hack to show new assignments in work tool on the courses list page</li>
+    <li>(3d51602 - BT#7016) Add reading and participation report to reporting tab for admins</li>
+    <li>(114fb7b - BT#7277) Add survey report to reporting tab for admins</li>
+    <li>(55f2c8a - BT#7154) Extend set_course_parameters in template.lib to provide _c array in templates - see developers guide</li>
+    <li>(a1bae6b - BT#7301) change exercise Id format to avoid excel treat it as a date</li>
+    <li>(71e0e9c - BT#7293) Fix transparency for transparent PNGs when resizing</li>
+    <li>(ccdb0c5 - BT#7316) check if user is also course_coach to allow exercise qualification</li>
+    <li>(3d26545, 629afbf) Use https when necessary (including Pixlr)</li>
+    <li>(94cc409 - BT#7392) Forcing Content-Type: text/html;utf8 for all pages</li>
+    <li>(a11f828 - BT#7345) Change Galician name order</li>
+    <li>(2fcd765 - BT#7356) studentview is only modified in local.inc.php</li>
+    <li>(977e5da) Allow platform's student to see courselog of courses they are admin</li>
+    <li>(5cf1da7 - BT#7273) Add "delete all" in works</li>
+    <li>(f1ca812 - BT#6638) Add delete button for hotpotatoes attempts</li>
+    <li>(ef5f4e4 - BT#4705) Add show_system_folders option in course</li>
+    <li>(72f22cc) Add images sprite for sidebar menu icons</li>
+    <li>(28e9d64 - BT#6418) Add attachments possibility in work comments</li>
+    <li>(56f4a78 - BT#6735) Add option $_configuration['show_simple_session_info']</li>
+    <li>(59de1f3) Add field Description (EnrichQuestion) in MultipleAnswer Excel test import</li>
+    <li>(75d0c15 - BT#7311) Fix error when creating a folder inside a group</li>
+    <li>(65119bc - BT#7682) Fix announcements bug in groups</li>
+    <li>(2a3adc5 - BT#7688) Teacher can see invisible elements during LP construction</li>
+    <li>(36486de - BT#7311) Fixing remove_XSS that always has ANONYMOUS even if you're in course</li>
+    <li>(02eb40b - BT#7682) Show forums that have a category = 0 in learning paths building</li>
+    <li>(efb50f8 - BT#7706) Removing COURSEMANAGERLOWSECURITY otherwise content will be truncated</li>
+    <li>(ea500a2 - BT#7780) Fix multiple true/false/doubt question</li>
+    <li>(e349c55 - BT#7763) Score per course report</li>
+    <li>(d628a49) Finetune CSS loading order in template.lib.php</li>
+    <li>(63d0324) Fix unintentional wildcard search on user_list</li>
+    <li>(ae5ab7f) Print the full path of the directories checked by Chamilo installation instead of just their pseudo–relative paths</li>
+    <li>(a5f40a2 - BT#7644) Change of conversion number from bytes to MB</li>
+    <li>(3ef2245) Fix phpCAS error on logout, and redirection loop</li>
+    <li>(86a3bc4 - BT#7297 - #7042) Setting $_configuration['save_user_last_login'] option. WARNING: this feature requires a database change - ignored if not present </li>
+    <li>(9e0e4e1 - BT#7870) Don't delete documents when removing an LP</li>
+    <li>(68d3c93) Table track_e_course_access now registers time in UTC</li>
+    <li>(9d0baf3 - BT#7930) Fix XSS problem when using Google Chrome and Youtube src</li>
+    <li>(64cda54) Removing PHP 5.2 validations</li>
+    <li>(4c6799f - BT#7803) Improving agenda UI, fixing event edition, Adding date-range-picker element in FormValidator, Updating datetimepicker js library, Adding sabre-vobject ical library</li>
+    <li>(0d0daa4 - BT#7780) Fix QTI import total question weight bug</li>
+    <li>(cac3061 - BT#7688) Fix tool LP link when copying a LP</li>
+    <li>(1e7a5c8) If event was marked as send to everyone clean the $sendToUsers list</li>
+    <li>(48d9010 - BT#7968) Remove disabled teachers from list of session course coaches</li>
+    <li>(f6b711f) Fix undefined watermark variable</li>
+    <li>(b4bd250 - BT#7975) Adding lp extra fields - WARNING requires database structure change - ignored if not applied</li>
+    <li>(0300ea0) Update course creation intro to show more relevant tools for teachers</li>
+    <li>(f5e8a0f - BT#8074) Shared and chat folders now filtered by session</li>
+    <li>(387cf2a - BT#8072) Show top menu bar even if empty to show logout button</li>
+    <li>(cd7b428) Sound notification of incoming messages in chat</li>
+    <li>(ef4d1ae) Add Adminer 4.1.0</li>
+    <li>(a346e5a) Reversed gradebook scores array to reduce confusion</li>
+    <li>(893d3ea) Fix issue getting last connexion date to a course (was getting it from the table track_e_course_access)</li>
+    <li>(40a1e95 - BT#7780) Fix issue in course URL inside documents copied from one course to another</li>
+    <li>(9b1ddc0, 82df5fb - BT#8207, BT#6768) Fix redirection issue in custom pages</li>
+    <li>(#7153) Fix partial scores, total score, PDF export, percentage score,  and certificate link in gradebook</li>
+    <li>(#5565) Fix installation bug (again)</li>
+    <li>(#7191) Fix SCORM autocompletion of parents</li>
 </ul>
 <h3>Stylesheets and theming</h3>
+Stylesheets have been considerably changed in version 1.9.8, which might require a little update on your side if you have a custom stylesheet. We're sorry about it, but it was really necessary to improve the adaptability of the interface for mobile devices (which we are sure you will appreciate). If you only changed the logo, we recommend you make a copy of an existing Chamilo style (main/css/chamilo*) again, rename it and simply replace the logo then upload the new style. If you have more complex styles, you might want to ask for the assistance of your web designer at the moment you update Chamilo to this version.
 <ul>
     <li>(#6217) Updated logo to new Chamilo logo in config section</li>
     <li>(#7007) Dropped support for IE7</li>
+    <li>(#7126) Improve styling for profile-attached files</li>
+    <li>(#7141) Great update to learning paths visualization</li>
+    <li>(#7142) Changed help image in learning paths</li>
+    <li>(#7147) Updated chat screen appearance</li>
+    <li>(d64b865 - BT#8230) Add "section-login" class in login pages</li>
 </ul>
 <h3>Web services</h3>
 <ul>
-    <li></li>
+    <li>(BT#6246) Improved sessions list in sessions_list.soap.php and added GetLearnpathHighestLessonLocation() (also BT#6667)</li>
+    <li>(BT#6246) Updated WSCreateUser() to manage active status, added WSGetUser(), WSGetUserFromUsername() and WSSubscribeUserToSessionSimple() and fixed WSListSessions() to allow sessions sales from Ubercart Drupal module</li>
+    <li>(43949c7) Add check on existence of extra fields (optional)</li>
+    <li>(#6763) Added WSUserSubscribedInCourse()</li>
+    <li>(5ddc3c0) Assume student status in EditUser* services and prevent modifying an admin to student</li>
 </ul>
 <h3>Removals</h3>
 <ul>
-  <li>Custom tabs can no longer be defined directly in the settings_current table.
+    <li>Custom tabs can no longer be defined directly in the settings_current table.
       If you have custom_tabs in this table (select * from settings_current where variable='show_tabs' AND subkey like 'custom_tab_%'), please add them through the homepage edition screen.</li>
+    <li>(#7180) Remove custom_tab_* feature </li>
 </ul>
 
+<h1>Chamilo 1.9.6.1 - 22th of May, 2014</h1>
+<h3>Release notes - summary</h3>
+<p>Chamilo 1.9.6.1 is a patch version with security patches on top of 1.9.6. Please check <a href="https://support.chamilo.org/projects/chamilo-18/wiki/Security_issues">our security issues page</a> for more information.</p>
+
 <h1>Chamilo 1.9.6 - Rochefort, 4th of June, 2013</h1>
 <h3>Release notes - summary</h3>
 <p>Chamilo 1.9.6 is a minor stable version with security patches and a series of improvements on top of 1.9.4.</p>

+ 20 - 12
documentation/credits.html

@@ -56,30 +56,31 @@
 In the following credits, when possible, we tried to put the latest contributorsfirst, in order to show more clearly the contributions relevant to the present time.<br />
 
 <a name="core-developers"></a>
-<h1>Core contributors</h1>
-<p>The core contributors are the people who contributed most to the Chamilo software. We are eternally thankful to them for having demonstrated a very high level of commitment, contributing their time and ideas to the project. Since version 1.9, we also include non-developer contributors as we consider their help is essential to our project.</p>
+<h1>Core contributors / Core team</h1>
+<p>The core contributors are the people who contributed most to the Chamilo software. We are eternally thankful to them for having demonstrated a very high level of commitment, contributing their time and ideas to the project. Since version 1.9, we also include non-developer contributors as we consider their help is essential to the success of our project. This list is updated with each version.</p>
 <h2>Chamilo LMS 1.9.*</h2>
 <ul>
+  <li>Yannick Warnier (yannick.warnier@beeznest.com) - Technical coordinator, Project Leader - BeezNest, Belgium/Peru</li>
   <li>Julio Allen Montoya Armas (gugli100@gmail.com) - Lead developer - BeezNest, France</li>
-  <li>Yannick Warnier (yannick.warnier@beeznest.com) - Technical coordinator, Project Leader - BeezNest, Peru</li>
-  <li>Yoselyn Castillo (yoselyn.castillo@beeznest.com) - Maintainer developer - BeezNest, Cuba</li>
-  <li>Coursenligne45 - Anonymous reporter and contributor to many bugfixes - France</li>
   <li>Hubert Borderiou - Developer responsible for exercises categories and many other practical fixes - Université de Grenoble, France</li>
-  <li>Juan Carlos Raña Trabado - Independent developer and mastermind for documents, wiki and other pedagogical tools - Spain</li>
-  <li>Laurent Opprecht - Developer responsible for rewriting/restructuring the application core and plugins - Université de Genève, Switzerland</li>
+  <li>Michela Mosquera - Community manager for the (very large) Latin American community - BeezNest, Peru</li>
+  <li>Coursenligne45 - Anonymous reporter and contributor to many bugfixes - Independent, France</li>
   <li>Noa Orizales - Tireless coordinator of everything non-related to coding, which enabled developers to do a better job - Contidos Dixitais, Spain</li>
-  <li>Marko Kastelic - Reporter and contributor to many bugfixes - Slovenia</li>
+  <li>Yoselyn Castillo (yoselyn.castillo@beeznest.com) - Maintainer developer - BeezNest, Cuba</li>
+  <li>Marko Kastelic - Reporter and contributor to many bugfixes - Independent, Slovenia</li>
+  <li>Juan Carlos Raña Trabado - Independent developer and mastermind for documents, wiki and other pedagogical tools - Independent, Spain</li>
+  <li>Laurent Opprecht - Developer responsible for rewriting/restructuring the application core and plugins - Université de Genève, Switzerland</li>
   <li>Special thanks go to <a href="#special-thanks">many other people</a>, less involved in the project directly but who's support was greatly appreciated</li>
 </ul>
 
 <h2>Previous versions</h2>
 <ul>
-  <li>BeezNest Latino (2007-2013) (aka Dokeos Latinoamérica from 2007-2009)
+  <li>BeezNest Latino (2007-2014) (aka Dokeos Latinoamérica from 2007-2009)
   <ul>
       <li>Yannick Warnier (ywarnier@beeznest.org) - Lead developer</li>
       <li>Julio Allen Montoya Armas (gugli100@gmail.com) - Lead Latino developer</li>
       <li>Christian Fasanando (christian534@hotmail.com) - 2009-2010</li>
-      <li>Yoselyn Castillo (yoselyn.castillo@beeznest.com) - Maintainer developer</li>
+      <li>Yoselyn Castillo (yoselyn.castillo@beeznest.com) - Developer</li>
     </ul>
     </li>
     <li>Independent (2008-2013)
@@ -638,7 +639,7 @@ Note that we are infinitely grateful to our translators, having made the spread
   <li> Jose Ramos Garces, for bug reports to 1.8.8</li>
   <li> André Boivin, for considerable number of bug reports and patches to HotPotatoes system and more bug reports in LMS 1.9.*</li>
   <li> Stéphane Lofreda, for bug reports to 1.8.8</li>
-  <li> Marko Kastelic, for bug reports to 1.8.8 & 1.9</li>
+  <li> Marko Kastelic, for bug reports to 1.8.8 &amp; 1.9</li>
   <li> Jorge Frisancho, USIL, for several small improvements to usability in 1.8.8 </li>
   <li> Jonas Fangstrom, for bug reports to 1.8.8</li>
   <li> Jeroen Brussich, for bug reports to 1.8.8</li>
@@ -682,14 +683,20 @@ Note that we are infinitely grateful to our translators, having made the spread
   <li>Gaëtan Frenoy, for efficient and well-coded updates to Drupal module and the integration with Drupal Ubercart in 1.9.8</li>
   <li>Francis Gonzales Tello, for a first draft of the OpenMeetings plugin for 1.9.8</li>
   <li>Maxim Solodovnik, for his help in the development of the OpenMeetings plugin for 1.9.8 - see https://issues.apache.org/jira/browse/OPENMEETINGS-802</li>
+  <li>Hector Lacherre, Alan Garcia, Genesis Lopez and Kenny Rodas, for their work extending Chamilo at USIL and letting us share the advance (notably for exercises tool and the tickets plugin) in 1.8 and 1.9</li>
   <li>Adolfo Igualada Martínez, for changes to the glossary to PDF export for subdir installs in 1.9.8 (#6929)</li>
   <li>Kevin Levron, for several contributions to Chamilo LMS 1.9.*</li>
+  <li>Cesar Perales, for several contributions to Chamilo LMS 1.9.*</li>
+  <li>Jérémie Grepilloux, for team work on getting issues reported for the University of Grenoble for 1.9.*</li>
+  <li>Guillaume Fosset, Aix-Marseilles Academy, for issues reporting and improvement suggestions in 1.9.*</li>
+  <li>Laurent Goossens, for Encora, Antwerp, for issues reporting and improvement suggestions in 1.9.* (most notably on the assignments tool)</li>
+  <li>Diego Escalante, Peru, for issues reporting and fixes in 1.9.*</li>
   <li>All the supporting parents, partners, children, friends, colleagues and sometimes students, of the very special geeks that we are, for their continous support and inspiration</li> 
 </ul>
 
 <a name="organisations"></a>
 <h1>Contributing organizations (only recorded since 2010)</h1>
-These institutions and companies have either contributed to the Chamilo project directly or through hiring services to contribute to Chamilo from an official provider:
+These institutions and companies have either contributed to a better free Chamilo LMS project directly or through hiring services to contribute to Chamilo from an official provider:
 <ul>
   <li>Académie d'Aix-Marseilles, France</li>
   <li>ACTA VZW, Belgium</li>
@@ -698,6 +705,7 @@ These institutions and companies have either contributed to the Chamilo project
   <li>BeezNest Latino, Lima, Peru</li>
   <li>Commission Scolaire des Hautes-Rivières, project TIC-FP, Canada</li>
   <li>Contidos Dixitais, Lugo, Spain</li>
+  <li>Encora/HBO5, Antwerp, Belgium</li>
   <li>EPSU, Genève, Switzerland</li>
   <li>ESIAJ, Namur, Belgium</li>
   <li>Formascience, Strasbourg, France</li>

+ 18 - 9
documentation/default.css

@@ -46,7 +46,7 @@ practical features. Some of this third-party software usually come bundled with
 your operating system, but some don't. This means that if that software is
 not installed on your Chamilo server, along with Chamilo, you might not enjoy 
 the best possible experience with your Chamilo platform. To avoid this problem,
-most of the sofware we used is bundled into a typical Chamilo install.</p>
+most of the sofware we used is bundled into a typical Chamilo installation.</p>
 <p>
 However, if such a third-party software is too big to be included, or if it 
 doesn't make sense to include it for whatever reason, you must make sure it is 
@@ -56,19 +56,28 @@ required information to install it.</p>
 
 <a name="dependencies"></a>
 <h2>Dependencies - client-side</h2>
+We recommend using HTML5-compatible technology.
 <ul>
-  <li>Any modern browser (IE6 and IE7 are obviously *excluded*). Safari is known to have small issues with the WYSIWYG editor.</li>
-  <li>Optional: Flash plugin (only required for a few features)</li>
+  <li>PC with a minimum of 512MB or any tablet or smartphone</li>
+  <li>We recommend a Pentium-I as a minimum processor capability for PCs</li>
+  <li>Works on Windows XP (SP3 recommended) and later versions, Linux (any version with a graphical interface), Mac OS (any version), FreeBSD, Android, iOS and pretty much any other operating system with a graphical interface you might think about</li>
+  <li>Any modern browser (IE6 and IE7 excluded). Safari is known to have small issues with the WYSIWYG editor.</li>
+  <li>Optional: Flash plugin (only required for a few features like the hotspot image question type and the audio recorder)</li>
+  <li>Optional: Java applet capability (only required in case you don't use the Flash version of the audio recorder)</li>
 </ul>
+
 <h2>Dependencies - server-side</h2>
 <ul>
-  <li> Apache 2+</li>
-  <li> PHP 5.3 or higher with MySQL bindings and php-gd extension to allow Chamilo to resize pictures. The
-  new components we have included from the Symfony framework highly depend on namespaces, a feature that appeared in PHP 5.3.0+</li>
-  <li> MySQL 5.1+ or any version of MariaDB database server</li>
-  <li>php5-intl</li>
-  <li>php5-curl (only required for a feature in the links tool)</li>
+  <li>Apache 2+</li>
+  <li> PHP 5.3 or higher with MySQL bindings (mysqlnd is recommended) and php-gd extension to allow Chamilo to resize pictures. The 
+  <li>MySQL 5.1+ or any version of MariaDB database server</li>
+  new components we have included from the Symfony framework highly depend on namespaces, a feature that appeared in PHP 5.3.0</li>
+  <li>php5-intl package (for international behaviour support)</li>
+  <li>php5-imagick</li>
+  <li>php5-curl (only required for a feature in the links tool and an optional installer check)</li>
   <li>php5-mcrypt (only required for a feature in the survey tool)</li>
+  <li>php5-ldap (only required for connection to a LDAP server)</li>
+  <li>php5-xapian (only required for full-text indexing and search)</li>
 </ul><br />
 <a name="included_dependencies"></a>
 <h2>Included Dependencies</h2>

+ 41 - 26
documentation/index.html

@@ -38,9 +38,11 @@
 
 <p>Thank you for downloading Chamilo</p>
 <ul>
-  <li>Test Chamilo on <a href="http://campus.chamilo.org">Free Campus</a></li>
+  <li>You can test Chamilo LMS, as teacher, without installing it, on <a href="http://campus.chamilo.org">Free Campus</a></li>
 </ul>
 <p>This guide explains how to install Chamilo FREE. Please read this fully before proceeding with the installation.</p>
+<p>Spanish: También puede leer <a href="installation_guide_es_ES.html">esta guía de instalación en español</a>.</p>
+<p>French: Vous pouvez aussi lire <a href="installation_guide_fr_FR.html">ce guide d'installation en français</a>.</p>
 
 
 <h2><b>Contents</b></h2>
@@ -60,7 +62,7 @@
   <li><a href="#11._Chamilo_rapid_ppt_conversion">Chamilo Rapid - PPT conversion system</a></li>
   <li><a href="#12._Setting_cron_up">Setting chronological tasks</a></li>
   <li><a href="#13._Changing_language_name_order">Changing the language's firstname/lastname order</a></li>
-  <li><a hreg="#14._Improving_files_download">Improving files download time</a></li>
+  <li><a href="#14._Improving_files_download">Improving files download time</a></li>
 </ol>
 
 <br />
@@ -117,7 +119,7 @@ and a deprecated single-database mode.</span><br />
 <h2><a name="2._Installation_of_Chamilo_LMS"></a><span style="font-weight: bold;">2. Installation of Chamilo LMS</span></h2>
 
 <ol>
-  <li><a href="http://www.chamilo.org/download">Download Chamilo LMS</a></li>
+  <li><a href="http://www.chamilo.org/en/download">Download Chamilo LMS</a></li>
   <li>Unzip it</li>
   <li>Copy the Chamilo directory in your Apache web directory. This can be
     <span style="font-weight: bold;">C:\xampp\htdocs\</span> on a Windows server or <span style="font-weight: bold;">/var/www/html/</span> (or /var/www/chamilo/) on a Linux server</li>
@@ -152,7 +154,7 @@ enable CSS styles package upload and sub-language definition:
   <li>[chamilo]/main/css/</li>
   <li>[chamilo]/main/lang/</li>
 </ul>
-Since Chamilo 1.8.8, you can also enable full-text indexing features which
+Starting from Chamilo 1.8.8, you can also enable full-text indexing features which
 require the php5-xapian PHP's extension module to be installed. If you do use
 it, you will need to allow your system to write into the sarchdb directory:
 <ul>
@@ -197,7 +199,7 @@ in production), the login and password for MySQL will probably remain empty.</p>
 
 <ul>
 
-  <li><strong>Protect your configuration directory: </strong>
+  <li><strong>Protect your configuration folder: </strong>
 make sure no one (but you) can overwrite it. You can find the config directory in
 <em>(chamilo folder)</em>/main/inc/conf.
 Make it read-only (windows/xwindows: right-click the file to edit the
@@ -226,7 +228,7 @@ directories). Don't hesitate to hire an experienced administrator to do that,
 it might be a bit more expensive now, but you'll be happy not to have to loose
 all of your data to a hacker who attacked your site.</li>
 
-  <li><strong>Configure your Chamilo installation: </strong>
+  <li><strong>Configure your Chamilo portal: </strong>
 in the administration section of Chamilo, you can use the Chamilo Configuration
 Settings to adjust the behavior of your installation.</li>
 
@@ -288,27 +290,27 @@ in your php.ini:</p>
 date.timezone = 'America/New_York'
 </div>
 
-<p><strong>Note:</strong> PHP 5.3.9 introduces a new setting "max_input_vars", which limits the number of elements you can send in one single form. If you are dealing with numerous users, make sure you set this setting higher than its default value of 1000.</p>
+<p><strong>Note:</strong> PHP 5.3.9 introduces a new setting "max_input_vars", which limits the number of elements you can send in one single form. If you are dealing with numerous users or very long learning paths (many items), make sure you set this setting higher than its default value of 1000.</p>
 
 <p><strong>BSD users:</strong> these php libraries have to be included during
  php installation:</p>
 
 <ul>
-  <li>php5-mysql The mysql shared extension for php</li>
-  <li>php5-pcre The pcre shared extension for php</li>
-  <li>php5-session The session shared extension for php</li>
-  <li>php5-zlib The zlib shared extension for php</li>
-  <li>(optional) php5-ldap if you want to be able to use LDAP authentication</li>
+    <li>php5-mysql (or php5-mysqlnd) The mysql shared extension for php</li>
+    <li>php5-pcre The pcre shared extension for php</li>
+    <li>php5-session The session shared extension for php</li>
+    <li>php5-zlib The zlib shared extension for php</li>
+    <li>php5-gd The graphics-generation extension for php</li>
+    <li>php5-intl The international behaviours extension for php</li>
+    <li>(optional) php5-ldap if you want to be able to use LDAP authentication</li>
 </ul>
 
 <p>You might also add the following php modules and packages:</p>
 <ul>
     <li>php5-ctype</li>
-    <li>php5-gd</li>
     <li>php5-iconv</li>
     <li>php5-json</li>
     <li>php5-mbstring</li>
-    <li>php5-intl</li>
     <li>php5-curl</li>
     <li>php5-iconv</li>
     <li>php5-xsl</li>
@@ -323,14 +325,19 @@ previous version of Chamilo LMS (1.*) or Dok€os (&lt;2.0)</h2>
 Before upgrading, we <b>heavily</b> recommend you do a full backup of the previous
 Chamilo or Dok€os directories and databases. If you are unsure how to achieve this
 please ask your hosting provider for advice.<br />
-<em>Chamilo LMS 1.9 comes with a new database structure. Although the upgrade script
-takes the migration in charge, it will be generate a very heavy load on your server
-during the upgrade, and will change your database completely, 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>
+<em>Chamilo LMS 1.9 comes with a new database structure in comparison to 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 completely, 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 />
+<div class="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 up to 20 times faster. If this is the kind of thing you
+need, we highly recommend you contact them (see reference below).</div>
 <div class="muted"> NOTE: This version of Chamilo can only be used to upgrade from
 smaller versions of Chamilo or Dok€os. For example, you cannot use the normal
 upgrade scripts from Chamilo 1.9 to upgrade from Dok€os 2.0. If you need this,
@@ -789,13 +796,21 @@ Provider</a> for) include a full review of the full-text search feature.
 <h2><a name="11._Chamilo_rapid_ppt_conversion"></a>Chamilo Rapid - PPT conversion system</h2>
 <p>
 <em>Note</em>: This step will require a dedicated server or a virtual dedicated server as the packages involved are not available on most shared hosting solutions.<br />
-On Debian or Ubuntu 11.10 and superior, install LibreOffice.org v3 and start it as a headless server:
+On Debian or Ubuntu 11.10 and superior, install LibreOffice v3 (or v4) and start it as a headless server:
 <pre>
-sudo apt-get install libreoffice
-sudo soffice -accept="socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;" -headless -nodefault -nofirststartwizard -nolockcheck -nologo -norestore &amp;
+sudo apt-get install libreoffice screen
+sudo screen
+su - www-data
+soffice --accept="socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;" --headless --nodefault --nofirststartwizard --nolockcheck --nologo --norestore
+CTRL+a, CTRL+d
 </pre>
-Then go to your administration page -&gt; <i>Chamilo Rapid</i> and set the host to "localhost" and the port to "2002". Save. Go to your course, learning path tool and see the new icon appeared. Import your PPT. This should work.
-<em>Note</em>: Sometimes, this doesn't work out so easily. You can probably ask for the assistance of any system administrator around with a bit of Java and PHP experience, or you can always ask one of the Chamilo's Official Providers for assistance (ask for a guaranteed commercial contract).
+Please note that this will effectively launch LibreOffice in a "headless" mode (thanks to the --headless option), in a "headless" terminal (thanks to screen). You can later get back into your "headless" terminal by launching:
+<pre>
+sudo screen -r
+</pre>
+You can then go to your administration page -&gt; <i>Chamilo Rapid</i> and set the host to "localhost" and the port to "2002". Save. Go to your course, learning path tool and see the new icon appeared. Import your PPT. This should work.
+<em>Note</em>: Sometimes, this doesn't work out so easily. You can probably ask for the assistance of any system administrator around with a bit of Java and PHP experience, or you can always ask one of the Chamilo's Official Providers for assistance (ask for a guaranteed commercial contract). The above is *not* meant for production servers with a high load. You should get a real system administrator to look at it and develop init scripts, in a way that makes monitoring and relaunching feasable. If any sysadmin wants to contribute this, feel free to contribute it to Chamilo: send us an e-mail at info@chamilo.org.
+<em>Note</em>: If you use LibreOffice 4, please note that version 4.2 (available in Ubuntu 14.04) has demonstrated to be more successfull at converting documents than version 4.1, which tended to crash when sending a document for conversion.
 </p>
 
 <hr style="width: 100%; height: 2px;" />

+ 10 - 7
documentation/installation_guide_es_ES.html

@@ -277,12 +277,14 @@ Antes de actualizar, le recomendamos <b>seriamente</b> que haga una copia de seg
 y bases de datos de Chamilo/Dokeos que previamente haya en su servidor. Si no está seguro de cómo hacer esto solicite
 asistencia a su proveedor de servicios de almacenamiento.
 </p>
-<p>
-Chamilo LMS 1.9 viene con una nueva estructura de base de datos. Aunque el script de actualización se encarga de la migración, se generará una carga muy alta en su servidor durante la actualización y además cambiará su base de datos completamente, preservando los datos tan bien como sea posible (se ha probado el procedimiento muchas veces, pero recuerde esto es GNU/GPLv3 y no se hace responsable de lo que le ocurran a sus datos sin supervisión profesional). Es por eso que * realmente * recomendamos realizar una copia de seguridad completa de su sistema antes de actualizar.
+<p><em>
+Chamilo LMS 1.9 viene con una nueva estructura de base de datos. Aunque el script de actualización se encarga de la migración, se generará una carga muy alta en su servidor durante la actualización y además cambiará su base de datos completamente, preservando los datos tan bien como sea posible (se ha probado el procedimiento muchas veces, pero recuerde esto es GNU/GPLv3 y no se hace responsable de lo que le ocurran a sus datos sin supervisión profesional). Es por eso que * realmente * recomendamos realizar una copia de seguridad completa de su sistema antes de actualizar.</em>
 </p>
-<p>
+<div class="muted"> NOTA: Para sistemas Chamilo con bases de datos muy pesadas, algunos de nuestros proveedores han desarrollado procedimientos usando más memoria pero acortando el tiempo necesario de la migración por hasta 20 veces. No dude en contactar con ellos (referencias más a bajo) si requiere de este tipo de servicios</div>
+</div>
+<div class="muted">
 NOTA: Esta versión de Chamilo sólo se puede utilizar para actualizar desde versiones inferiores de Chamilo o Dok€os. Por ejemplo, no puede utilizar los scripts de actualización normales de Chamilo 1.9 para actualizar desde Dok€oS 2.0. Si usted necesita esto, por favor póngase en contacto con uno de los proveedores oficiales de la Asociación Chamilo)
-</p>
+</div>
 
 <h3>3.1 Actualizar desde Chamilo 1.9.x (actualización menor)</h3>
 <p>
@@ -727,14 +729,15 @@ Nota: Este paso requiere un servidor dedicado o un servidor dedicado virtual ya
 En Debian o Ubuntu 11.10 y superior, instale LibreOffice.org v3 e iniciarlo como un servidor en segundo plano:
 <pre>
 sudo apt-get install libreoffice
-sudo soffice -accept="socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;" -headless -nodefault -nofirststartwizard -nolockcheck -nologo -norestore &amp;
+sudo soffice --accept="socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;" --headless --nodefault --nofirststartwizard --nolockcheck --nologo --norestore &amp;
 </pre>
 </p>
 <p>
-A continuación, vaya a la página de administración -&gt; Chamilo Rapid y establecer el host a "localhost" y el puerto a "2002". Guarde los cambios. Vaya a su curso, en la pantalla de herramientas de aprendizaje y verá que apareció un nuevo icono. Importar tu PPT. Esto debería funcionar.
+A continuación, vaya a la página de administración -&gt; Chamilo Rapid y establecer el host a "localhost" y el puerto a "2002". Guarde los cambios. Vaya a su curso, en la pantalla de herramientas de aprendizaje y verá que apareció un nuevo icono. Importar tu PPT. Esto debería funcionar. Otra possibilidad es lanzar soffice desde una instancia de "screen", sin el &amp; al final.
 </p>
 <p>
-Nota: A veces, esto no se resuelve tan fácilmente. Usted probablemente tenga que solicitar la asistencia de algún administrador de sistemas con un poco de experiencia en  Java y PHP, o siempre podrá preguntar a uno de los proveedores oficiales de Chamilo para asistirle (pregunte por un contrato comercial garantizado).
+<em>Nota</em>: A veces, esto no se resuelve tan fácilmente. Usted probablemente tenga que solicitar la asistencia de algún administrador de sistemas con un poco de experiencia en  Java y PHP, o siempre podrá preguntar a uno de los proveedores oficiales de Chamilo para asistirle (pregunte por un contrato comercial garantizado).
+<em>Nota</em>: Si usa la versión 4 de LibreOffice, es importante notar que la versión 4.2 (disponible en Ubuntu 14.04) ha mostrado resultados considerablemente más exitosos al momento de convertir documentos que la versión 4.1 (disponible en Ubuntu 13.10), la cual tiene tendencia a colgarse durante las conversiones.
 </p>
 
 <hr style="width: 100%; height: 2px;" />

+ 922 - 0
documentation/installation_guide_fr_FR.html

@@ -0,0 +1,922 @@
+<html lang="en">
+<head>
+    <meta charset="utf-8" />
+    <title>Guide d'installation de Chamilo</title>
+    <link rel="stylesheet" href="../main/css/base.css" type="text/css" media="screen,projection" />
+    <link rel="stylesheet" href="default.css" type="text/css" media="screen,projection" />
+    <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>
+<body>
+    <div class="navbar navbar-fixed-top">
+    <div class="navbar-inner">
+        <div class="container-fluid">
+            <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+            </a>
+            <a class="brand" href="index.html">Chamilo - Documentation</a>
+            <div class="nav-collapse">
+            <ul class="nav">
+                <li><a href="index.html">Home</a></li>
+                <li ><a href="readme.html">About</a></li>
+                <li ><a href="license.html">License</a></li>
+                <li><a href="credits.html">Credits</a></li>
+                <li><a href="dependencies.html">Dependencies</a></li>
+                <li><a href="changelog.html">Changelog</a></li>
+            </ul>
+            </div><!--/.nav-collapse -->
+        </div>
+    </div>
+</div>
+
+<div class="container">
+<h1>Guide d'installation de Chamilo LMS</h1>
+
+<p>Merci d'avoir téléchargé Chamilo</p>
+<ul>
+  <li>Vous pouvez tester Chamilo LMS, en tnt que professeur, sans l'installer, sur <a href="http://campus.chamilo.org/?language=french">le campus libre de Chamilo</a></li>
+</ul>
+<p>Ce guide explique comment installer Chamilo LMS. Nous recommandons activement la lecture complète de ce guide avant de commencer l'installation.</p>
+<p>English: Get back to the <a href="installation_guide.html">English version of the installation guide</a>.</p>
+
+
+<h2><b>Index</b></h2>
+
+<ol>
+
+  <li><a href="#1._Pre-requisites">Prérequis</a></li>
+  <li><a href="#2._Installation_of_Chamilo_LMS">Installation de Chamilo LMS</a></li>
+  <li><a href="#3._Upgrade_from_a_previous_version_of">Upgrade depuis une version antérieure de Chamilo/Dok€os</a></li>
+  <li><a href="#4._Troubleshooting">Problèmes</a></li>
+  <li><a href="#5._Administration_section">La section d'administration</a></li>
+  <li><a href="#6._LDAP">LDAP&nbsp;</a></li>
+  <li><a href="#7._Mathematical_formulas">Formules mathématiques avec LaTeX</a></li>
+  <li><a href="#8._ASCIIMathML_mathematical_formulas">Formules mathématiques avec ASCIIMathML</a></li>
+  <li><a href="#9._WIRIS_mathematical_formulas">Formules mathématiques avec WIRIS</a></li>
+  <li><a href="#10._Xapian_fulltext_indexing">Indexation full-text avec Xapian</a></li>
+  <li><a href="#11._Chamilo_rapid_ppt_conversion">Chamilo Rapid - système de conversion PPT</a></li>
+  <li><a href="#12._Setting_cron_up">Configurer de tâches "chronologiques"</a></li>
+  <li><a href="#13._Changing_language_name_order">Changer l'ordre des noms/prénoms par langue</a></li>
+  <li><a href="#14._Improving_files_download">Améliorer le temps de téléchargement de fichiers</a></li>
+</ol>
+
+<br />
+<hr style="width: 100%; height: 2px;" />
+<h2><a name="1._Pre-requisites"></a>1. Prérequis</h2>
+
+Chamilo peut être installé sous Windows, Linux, Max OS X et serveurs UNIX
+indifféremment. Cela dit, nous recommandons activement l'utilisation d'un
+serveur Linux pour sa flexibilité optimale, son facile contrôle à distance
+et sa capacité de redimensionnement. En particulier, nous recommandons la
+distribution Debian (ou sa petite soeur Ubuntu)<br />
+
+<br />
+
+Chamilo est un système de gestion de l'apprentissage (LMS) qui fonctionne
+principallement sur base d'un <span style="font-weight: bold;">serveur web (Apache 2.2
+recommandé)</span>, de <span style="font-weight: bold;">MySQL (5.1 ou 5.5) ou de MariaDB
+(5 ou 10)</span> et de <span style="font-weight: bold;">PHP 5.3, 5.4 ou 5.5 (5.5 recommandé
+pour une plus grande efficacité)</span> (la trilogie aussi appelée
+<span style="font-weight: bold;">AMP</span>). <br />
+
+
+<br />
+
+Tous ces éléments logiciels sont sous licence de logiciels libres et sont
+disponibles gratuitement.<br />
+
+<br />
+
+Pour faire fonctionner Chamilo <span style="font-weight: bold;">LMS</span> sur votre serveur,
+il est suggéré d'installer WAMP, LAMP ou MAMP (ou leurs composants
+individuellement):
+<ul>
+
+  <li>Pour installer <span style="font-weight: bold;">WAMP</span> (AMP sous Windows), nous
+      recommandons le paquet
+      <a href="http://www.apachefriends.org/en/xampp.html">XAMPP</a> en
+      installateur ".exe"</li>
+
+  <li>Pour installer <span style="font-weight: bold;">LAMP</span>
+(AMP sous Linux), utilisez le système de gestion de paquets de votre
+      distribution préférée (apt, Synaptic, RPMFinder, yum etc.). Par exemple,
+      sur un serveur Ubuntu, vous pouvez utiliser le Shell (terminal) ou
+      Synaptic via le guide
+    <a href="http://ubuntuguide.org/wiki/Ubuntu:Feisty#Apache_HTTP_Server">
+        Ubuntuguide on Apache</a> et les sections suivantes du présent guide/li>
+
+  <li>Pour installer <span style="font-weight: bold;">MAMP</span> (AMP sur Mac OS X),
+    référez-vous au site dédié de
+    <a href="http://www.mamp.info/en/index.php">MAMP</a></li>
+
+  <li>Assurez-vous de vérifier les
+      <a href="dependencies.html" title="Dependencies">dépendances</a> si vous
+      avez besoin de connaîtres les pré-requis de façon plus précise.
+</ul>
+
+
+<br />
+
+
+<h3><span style="font-weight: bold;">Serveurs de base de données MySQL ou MariaDB</span></h3>
+
+Vous aurez besoin d'un login et d'un mot de passe pour gérer et créer une base
+de données. Généralement, la configuration par défaut sur des ordinateurs
+locaux est de permettre la connexion comme utilisateur root avec un mot de passe
+vide. Il est FORTEMENT RECOMMANDÉ de modifier le mot de passe et de définir un
+utilisateur avec un accès à une (et une seule) base de données spécifique.
+Veuillez vous référer à la documentation de MySQL ou MariaDB pour ce faire.
+<br />
+<span class="muted">Veuillez noter que ceci a été largement simplifié depuis la
+    version 1.9, étant donné qu'il était nécessaire antérieurement de choisir
+    entre plusieurs bases de données ou un mode base-de-données-unique
+    non maintenu..</span><br />
+
+<hr style="width: 100%; height: 2px;" />
+<h2><a name="2._Installation_of_Chamilo_LMS"></a><span style="font-weight: bold;">2. Installation de Chamilo LMS</span></h2>
+Une fois votre système de base préparé, vous pouvez commencer l'installation de Chamilo proprement dite
+<ol>
+  <li><a href="http://www.chamilo.org/download">Télécharger Chamilo LMS</a></li>
+  <li>Décompressez-le</li>
+  <li>Copiez le répertoire de Chamilo dans le répertoire web de votre serveur web (Apache).
+      Celui-ci peut se trouver dans <span style="font-weight: bold;">C:\xampp\htdocs\</span> sur
+      une machine Windows avec WAMP, ou dans <span style="font-weight: bold;">/var/www/html/</span> (ou /var/www/) sur un serveur Linux</li>
+  <li>Ouvrez votre navigateur (Firefox, Chrome, ...) et tapez l'adresse suivante
+    <span style="font-weight: bold;">http://localhost/chamilo/</span> si vous l'avez installé localement ou
+    <span style="font-weight: bold;">http://www.domain.com/chamilo/</span> l'avez installé à distance. <span class="muted">Nous recommandons la définition d'un Virtual Host spécifique pour cette installation si vous avez les compétences requises pour configurer votre serveur web</span>
+    </li>
+
+  <li>Suivez le processus d'installation. Vous pouvez accepter toutes les
+      valeurs par défaut. Pensez à modifier le mot de passe d'administrateur
+      afin de vous en souvenir plus tard.</li>
+</ol>
+<br />
+<span class="muted">Note: si vous installez Chamilo localement en utilisant
+"localhost" ou l'adresse IP de votre ordinateur durant l'installation, vous
+pourriez rencontrer des difficultés lors de l'accès depuis d'autres ordinateurs.
+Pour éviter ça, vous pouvez
+<a href="http://beeznest.wordpress.com/2013/01/15/answering-to-different-addresses-with-chamilo/">appliquer une petite modification à votre
+fichier de configuration</a>.</span>
+<br /> <br />
+Les répertoires suivants doivent êtres lisibles, éditables et exécutables par votre serveur web.
+<br />
+<span class="muted">Note: Cela ne requiert généralement pas de modification spécifique
+    sous Windows (ce qui est d'ailleurs l'une des raisons principales pour
+    l'existence d'autant de virus et malware pour ce système d'exploitation,
+    mais cela demande l'utilisation de "chmod" sous Linux et Mac. Dans ces
+    derniers cas, veuillez suivre les instructions qui suivent.</span>
+<br /><br />
+Remplacez [chamilo] par le répertoire où vous avez placé le contenu du zip:
+<ul>
+  <li>[chamilo]/main/inc/conf/</li>
+  <li>[chamilo]/main/upload/users/</li>
+  <li>[chamilo]/main/default_course_document/images/</li>
+  <li>[chamilo]/archive/</li>
+  <li>[chamilo]/courses/</li>
+  <li>[chamilo]/home/</li>
+</ul>
+Optionnellement, vous pouvez faire la même chose pour les répertoires suivants
+si vous souhaitez permettre l'envoi de styles CSS ou la définition de nouvelles
+langues par les administrateurs de Chamilo:
+<ul>
+  <li>[chamilo]/main/css/</li>
+  <li>[chamilo]/main/lang/</li>
+</ul>
+Depuis Chamilo 1.8.8 (et pour toutes les versions suivantes), vous pouvez
+également activer l'indexation full-text, qui requiert l'installation de
+l'extension php5-xapian. Si vous l'utilisez, vous devrez autoriser votre système
+à écrire dans le répertoire searchdb:
+<ul>
+  <li>[chamilo]/searchdb</li>
+</ul>
+Si vous trouvez un répertoire tests/ à la racine, veuillez le supprimer.
+Il s'agit d'un répertoire de développement qui ne bénéficie pas des
+vérifications de sécurité nécessaires pour un système en production. Il ne
+devrait en tout cas *jamais* être accessible aux utilisateurs finaux sur un
+système en production.
+
+<p>Sous les systèmes d'exploitation Linux, Mac OS X et BSD, vous pouvez utiliser
+la commande <em>chmod 0775</em> pour modifier les permissions, bien que nous
+recommandions, pour des raisons de sécurité, de cherchez l'assistance d'un
+administrateur système expérimenté pour le faire.
+Sous Windows, vous pourriez avoir besoin de vérifier les propriétés de ces
+répertoires (en cliquant dessus avec le bouton droit de la souris).</p>
+
+<p>Le répertoire suivant doit être lisible et éditable <em>seulement</em>
+durant le processus d'installation:</p>
+<ul>
+  <li>chamilo/main/inc/conf/</li>
+</ul>
+
+<p>
+<b>NOTES:</b><br />
+
+Ne modifiez *pas* les fichiers home/home_*.html directement. Choisissez plutôt
+l'option "Configurer la page d'accueil" dans la section d'administration de
+    Chamilo.</p>
+
+<p><span style="font-weight: bold;">Windows</span> : avec les paquets tout-en-un comme XAMPP,
+vous pouvez installer Chamilo facilement. Dans ce cas (et à moins que vous ne
+l'utilisiez en production), le mot de passe de MySQL pourrait rester vide.</p>
+
+<p></p>
+
+
+<h3><strong> Configuration et sécurité après l'installation</strong></h3>
+
+<ul>
+
+  <li><strong>Protégez votre répertoire de configuration: </strong>
+assurez-vous que personne (à part vous) ne puisse l'éditer.
+Vous trouverez le répertoire de configuration dans <em>(chamilo folder)</em>/main/inc/conf.
+Rendez-le lisible uniquement (windows/xwindows: click-droit sur le répertoire
+pour en éditer les propriétés - Linux/BSD/MacOSX: utilisez la commande "chmod -r 0555".
+Le fichier de configuration (main/inc/conf/configuration.php) est créé par votre serveur webThe config
+file is created by your web server (Apache + PHP, usually) so you may
+need to be root user to change its permissions.</li>
+
+  <li><strong>Protégez votre répertoire d'installation: </strong>
+si le répertoire <em>[chamilo]</em>/main/install est toujours accessible,
+des individus mal intentionnés pourraient (potentiellement) provoquer une
+réinstallation par dessus votre installation actuelle (ce qui pourrait se
+matérialiser en une perte de données). Pour éviter ça, déplacez le répertoire
+ailleurs, changez ses permissions ou supprimez-le, tout simplement, afin que
+personne ne puisse y accéder.</li>
+
+  <li><strong>Astuces de sécurité: </strong>
+rendre tous les fichiers éditables par tout le monde (chmod -r 0777) vous
+permettra d'installer Chamilo plus facilement, il est vrai, mais cela permettra
+également à d'éventuels individus malintentionés de plus facilement attaquer
+votre installation de Chamilo. Il est donc conseillé d'utiliser un utilisateur
+système spécifique comme propriétaire de tous les fichiers et répertoires de
+Chamilo, et de seulement donner accès au serveur web sur les répertoires et
+fichiers énumérés plus haut.
+Ces fichiers et répertoires doivent uniquement être accessibles au serveur web,
+pas au reste des utilisateurs système sur votre serveur, et encore moins aux
+utilisateurs non-identifiés qui tenteraient de se connecter à votre serveur (par
+exemple via FTP). Il est aussi recommandé d'empêcher l'interprétation de
+fichiers PHP pour tous les répertoires qui autorisent l'écriture par le serveur
+web (à part la racine des répertoires de cours).
+N'hésitez pas à engager un administrateur système expérimenté pour cela. Cela
+peut s'avérer un peu plus cher maintenant, mais vous serez heureux de ne pas
+devoir faire façe aux coûts exhorbitants des pertes de données au cas où vous
+souffririez l'attaque d'un pirate.</li>
+
+  <li><strong>Configurez votre installation de Chamilo: </strong>
+dans la section d'administration de Chamilo, vous pouvez utiliser les paramètres
+de configuration pour ajuster le comportement de votre installation.</li>
+
+  <li><strong>Configurez les mails de Chamilo: </strong>
+la plupart des installations de Chamilo utilisent la configuration de mails par
+défaut de php.ini. Cependant, si vous utilisez un serveur de mails différent,
+vous pourriez avoir besoin d'ajuster les paramètres de mails dans le fichier
+<em>[chamilo]</em>/main/inc/conf/mail.conf.php.</li>
+
+  <li>Jetez un oeil sur notre<a href="./security.html">guide de sécurité</a> pour plus d'information</li>
+</ul>
+
+<br />
+
+
+<h3>Configuration de PHP</h3>
+
+
+Pour obtenir le meilleur de votre installation de Chamilo, vous devez configurer vos paramètres PHP: <br />
+
+<ul>
+
+  <li>Editez le fichier php.ini (sous windows, il devrait se trouver dans
+  <span style="font-weight: bold;">C:\xampp\php\php.ini</span>,
+  et sous Debian ou Ubuntu (Linux) :
+  <span syle="font-weight: bold;">/etc/php5/apache2/php.ini</span></li>
+  <li>cherchez les mots contenant "_max" et augmentez les valeurs correspondantes
+      pour permettre au serveur de gérer des fichiers plus grands et des requêtes plus lourdes</li>
+  <li>nous recommandons les valeurs suivantes : </li>
+</ul>
+
+<div class="code">
+      max_execution_time = 300 ;Maximum execution time of each script, in seconds<br />
+      max_input_time = 600 ;Maximum amount of time each script may spend parsing request data<br />
+      memory_limit = 256M ;Maximum amount of memory a script may consume (128MB)<br />
+      post_max_size = 100M<br />
+      upload_max_filesize = 100M<br />
+</div>
+
+<p>Certains de nos utilisateurs ont rencontré des problèmes parce que leurs
+    paramètres de configuration ne correspondaient pas dans les cas suivants (valeurs recommandées):</p>
+
+<ul>
+  <li>short_open_tag       = Off</li>
+  <li>safe_mode            = Off</li>
+  <li>magic_quotes_gpc     = Off</li>
+  <li>magic_quotes_runtime = Off</li>
+</ul>
+
+<p>
+Certaines versions antérieures de Chamilo exigeaient que register_globals soit
+configuré à On. Ce n'est plus le cas depuis bien longtemps et ce paramètres
+devrait désormais être configuré à Off (bien que les dernières versions de PHP
+ne proposent même plus ce paramètre de configuration et le mettent d'office à Off).
+<span class="muted">L'activation du register_globals est désormais largement considérée comme un mauvais choix au niveau sécurité.</a></p>
+
+<p><strong>Note:</strong> si vous utilisez PHP 5.3 ou supérieur, vous
+<em>devez</em> configurer le paramètre <em>date.timezone</em> à la zone horaire
+de votre serveur. Par exemple, si votre serveur est dans la zone horaire
+'America/New_York', configurez-le de la manière suivante dans votre php.ini:</p>
+
+<div class="code">
+date.timezone = 'America/New_York'
+</div>
+
+<p><strong>Note:</strong> PHP 5.3.9 introduit un nouveau paramètre appelé
+"max_input_vars", qui limite le nombre d'éléments que vous pouvez envoyer au
+travers d'un formulaire déterminé. Si vous devez gérer de grandes quantités
+d'utilisateurs ou de longs parcours d'apprentissage (de nombreux éléments),
+assurez-vous de configurer ce paramètre plus haut que se valeur par défault
+de 1000.</p>
+
+<p><strong>Utilisateurs de BSD:</strong> les librairies suivantes doivent être
+inclues durant l'installation de PHP:</p>
+
+<ul>
+    <li>php5-mysql (or php5-mysqlnd) The mysql shared extension for php</li>
+    <li>php5-pcre The pcre shared extension for php</li>
+    <li>php5-session The session shared extension for php</li>
+    <li>php5-zlib The zlib shared extension for php</li>
+    <li>php5-gd The graphics-generation extension for php</li>
+    <li>php5-intl The international behaviours extension for php</li>
+    <li>(optional) php5-ldap if you want to be able to use LDAP authentication</li>
+</ul>
+
+<p>Nous recommandons aussi les extensions suivantes:</p>
+<ul>
+    <li>php5-ctype</li>
+    <li>php5-iconv</li>
+    <li>php5-json</li>
+    <li>php5-mbstring</li>
+    <li>php5-curl</li>
+    <li>php5-iconv</li>
+    <li>php5-xsl</li>
+    <li>php5-ldap</li>
+    <li>php5-xapian</li>
+</ul>
+
+<div style="color: red;">Le reste de ce manuel n'est pas encore traduit. N'hésitez pas à consulter
+le <a href="http://www.chamilo.org/forum">forum de Chamilo</a> si la lecture
+en anglais vous est impossible.</div>
+
+<hr />
+<h2><a name="3._Upgrade_from_a_previous_version_of"></a>3. Mettre à jour depuis
+une version antérieure de Chamilo LMS (1.*) ou de Dok€os (&lt;2.0)</h2>
+Avant de mettre à jour, nous recommandons chaudement la prise d'une copie de
+sauvegarde de votre installation (répertoires et bases de données).
+Si vous ne savez pas comment faire, consultez votre fournisseur d'hébergement
+ou un administrateur système qualifié.<br />
+<em>Chamilo LMS 1.9 comes with a new database structure in comparison to 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 completely, 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 />
+<div class="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 up to 20 times faster. If this is the kind of thing you
+need, we highly recommend you contact them (see reference below).</div>
+<div class="muted"> NOTE: This version of Chamilo can only be used to upgrade from
+smaller versions of Chamilo or Dok€os. For example, you cannot use the normal
+upgrade scripts from Chamilo 1.9 to upgrade from Dok€os 2.0. If you need this,
+please contact one of the Chamilo Association's official providers &lt;providers@chamilo.org&gt;)</div>
+
+<p></p>
+
+<h3>3.1 Upgrading from Chamilo 1.9.x (minor upgrade)</h3>
+As this is only a minor version change from previous 1.9.* 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="muted">(if you have, make sure you keep a copy on the side*)</span></li>
+  <li> download the Chamilo 1.9 install package from the <a href="http://www.chamilo.org/download">Chamilo download page</a></li>
+  <li> unzip the new files of Chamilo 1.9 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> edit the main/inc/conf/configuration.php file: at the en of the file, locate the previous version number (e.g. '1.9.4') and change it to this new version (e.g. '1.9.8')</li>
+  <li> you're done! No other upgrade procedure is required</li>
+</ul>
+
+<br />
+
+
+<h3>3.2 Upgrading from Chamilo 1.8.x</h3>
+<ul>
+  <li> check that you haven't left any customised stylesheet or image <span class="muted">(if you have, make sure you keep a copy on the side*)</span></li>
+  <li> download the Chamilo 1.9 install package from the <a href="http://www.chamilo.org/download">Chamilo download page</a></li>
+  <li> unzip the new files of Chamilo 1.9 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> point your browser on your portal URL + main/install/</li>
+  <li> choose your language and click&nbsp;<span style="font-style: italic;">Upgrade from 1.8.x</span></li>
+</ul>
+
+<br />
+
+<h3>3.3 Upgrading from Dok€os 1.8.x</h3>
+
+<em>If you upgrade from Dok€os 1.8.x</em> :&nbsp;
+<ul>
+  <li> check that you haven't left any customised stylesheet or image <span class="muted">(if you have, make sure you keep a copy on the side*)</span></li>
+  <li> download the Chamilo 1.9 install package from the <a href="http://www.chamilo.org/download">Chamilo download page</a></li>
+  <li> unzip the new files of Chamilo 1.9 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> point your browser on your portal URL + main/install/</li>
+  <li> choose your language and click&nbsp;<span style="font-style: italic;">Upgrade from 1.8.x</span></li>
+</ul>
+A bunch of Chamilo administrators have reported minor issues with the migration between versions considerably apart (like from Dok€os to Chamilo).
+This might include loosing some assignments or forum posts. To avoid any ugly effect on your users, we recommend you first establish a checklist
+of all the content that is critical to you, and that you keep a working copy of your previous site on the side. This way, it will be easier to
+manage the transition by letting your users get access to their contents from the previous site and making it easier for you to compare the two.
+If you are experiencing difficulties, consider asking for help from a PHP developer or hiring an <a href="http://www.chamilo.org/en/providers" target="_blank">official Chamilo provider</a>. They will make sure
+you get the best our of your Chamilo migration.
+<br />
+
+<span class="muted">* Styles and images are located in the main/css or main/img
+directories. You can still recover them from your backup if you have made it.
+ Any modified style or image that uses the default style/image name will be
+ overwritten by the next step. To avoid loosing your customisations, always
+ ensure you copy the styles/images under a new name and use and modify the
+ copy, not the original. The original will always be overwritten by newer
+ versions. In Dok€os 1.8.5, we have changed the name of several CSS themes.
+ Backwards compatibility is ensured by the fact that an upgrade only adds the
+new themes, but you should try and use these new themes rather than sticking
+to the old ones which will be deprecated shortly (not maintained).</span>
+<br />
+
+<p></p>
+
+<h3>3.4 Upgrading from Dok€os 1.6.x</h3>
+
+
+<em>If you upgrade from a lower version of Dok€os (1.6.x)</em>, the first
+and most important thing to do is to *move* your current directory to another
+place.<br />
+An easy way to do that is to create a subdirectory called "old_version"
+in your current Chamilo/Dok€os directory and move everything in there using a
+simple "move" command (i.e. under Linux: mkdir old_version; mv *
+old_verion/), then make the old_version/ directory writeable by the web
+server so that courses/ and upload/ directories can be moved from the
+old to the new installation.<br />
+<br />
+The complete process is as follow:
+<ul>
+  <li> move the current Dok€os directory contents to a subdirectory called
+    <em>old_version</em> and make it writeable by the web server. This
+is important to allow the move of the courses/ and upload/ directories
+to the new install</li>
+  <li> download the Chamilo 1.9 install package from the
+    <a href="http://www.chamilo.org/download">Chamilo download page</a></li>
+  <li> unzip the new files of Chamilo 1.9 in the main Chamilo/Dok€os directory. The
+new directory <em>main</em> should be located directly inside your
+Chamilo/Dok€os root folder</li>
+  <li> point your browser on your portal URL</li>
+  <li> choose your language and click&nbsp;<span style="font-style: italic;">Upgrade from 1.6.x</span> and confirm the
+current directory of the old version</li>
+</ul>
+
+<br />
+
+<p class="muted"> NOTE: The upgrade from 1.6.x to 1.8.x implies a revision of
+the customised graphics and styles. The new version uses a complete new
+set of icons and styles, which means the ones from version 1.6 cannot be
+simply reused. The good news is the version 1.8.x allows you to create
+your own style in a separate css folder, that you can then reuse through
+all the 1.8.x versions
+</p>
+
+<p>
+<b>WARNING:</b><br />
+
+Do not delete the previous Chamilo/Dok€os installation directory before installing
+the new one. When the update is successfully finished, you can remove
+the old path.</p>
+
+
+<p class="muted">
+<b>NOTE:</b><br />
+
+Do not modify the home_*.html files directly. Instead,
+choose "Configure the homepage" in the Chamilo administration section.</p>
+
+<p></p>
+<h3>3.5 In both last cases</h3>
+
+The following directories need to be readable, writeable and executable for the web server:
+<ul>
+  <li>chamilo/main/inc/conf/</li>
+  <li>chamilo/main/upload/users/</li>
+  <li>chamilo/main/default_course_document/</li>
+  <li>chamilo/archive/</li>
+  <li>chamilo/courses/</li>
+  <li>chamilo/home/</li>
+</ul>
+
+On Linux, Mac OS X and BSD operating systems you can quick-fix this using the
+<em>chmod 0777</em> command, but if you are unsure, we recommend you seek advice for
+your own OS on <a href="http://www.chamilo.org/forum">our forum</a>.
+In Windows, you may need to check the properties of the folders.
+<p></p>
+<h3>3.6 Quick-upgrade from 1.8.x guide for Linux</h3>
+
+
+The following quick-upgrade guide assumes that:
+<ul>
+  <li>the Chamilo database username (for MySQL) is "chamilo_db_user" and your login is "chamilo_user"</li>
+  <li>the chamilo installation is currently in /var/www/chamilo/ and it has 0777 permissions (which is bad, we know, but you'll fix it)</li>
+  <li>your portal's URL is http://www.portalurl.com/</li>
+</ul>
+
+<br />
+
+
+On the command-line, type:
+<ul>
+  <li>cd /tmp</li>
+  <li>mysqldump -u chamilo_db_user -p --all-databases --result-file=/home/chamilo_user/chamilo_old.sql</li>
+  <li>cp -ra /var/www/chamilo /home/chamilo_user/backup_chamilo</li>
+  <li>mkdir /var/www/chamilo/old_version</li>
+  <li>mv /var/www/chamilo/* /var/www/chamilo/old_version/</li>
+  <li>chmod -R 0777 /var/www/chamilo/old_version/</li>
+  <li>wget http://chamilo.googlecode.com/files/chamilo-1.9.8.tar.gz</li>
+  <li>tar zxvf chamilo-1.9.8.tar.gz</li>
+  <li>sudo cp -ra chamilo-1.9.8/* /var/www/chamilo/</li>
+  <li>rm chamilo-1.9.8.tar.gz</li>
+  <li>sudo rm -r chamilo-1.9.8/</li>
+</ul>
+
+<br />
+
+Then:
+<ul>
+  <li>Direct your browser to http://www.portalurl.com/main/install/</li>
+  <li>Proceed with the installation</li>
+  <li>Review the directories permissions</li>
+</ul>
+
+<p></p>
+
+<p></p>
+
+
+<hr style="width: 100%; height: 2px;" />
+
+<h2><a name="4._Troubleshooting"></a>4. Troubleshooting</h2>
+
+
+<p>If you have&nbsp;problems, go to the <a href="http://www.chamilo.org">Chamilo website</a> and ask a question on the
+<a href="http://www.chamilo.forum">support forum</a>. Please read the previous messages first to see if there is
+already an answer to your question.</p>
+
+
+<h2></h2>
+
+
+<hr style="width: 100%; height: 2px;" />
+<h2><a name="5._Administration_section"></a>5. Administration section</h2>
+
+<p>To access the Chamilo administration section, open browser,
+go to your Chamilo adress and log in with the admin user.
+Then you will see a "Platform admin section" link in the header of the
+web page. There you can manage users, courses, sessions, portal look
+and feel, homepage content, course categories etc.&nbsp;</p>
+
+<p></p>
+<hr style="width: 100%; height: 2px;" />
+<h2><a name="6._LDAP"></a>6. LDAP</h2>
+
+<p>
+<i>This part is optional, only organisations with an LDAP server will need to read this.</i><br />
+
+An LDAP module is already provided in Chamilo, but it has to be configured to make it work.</p>
+
+
+<h3><b>Compiling</b></h3>
+
+<p>
+Linux servers: It's possible that you have to recompile php with ldap support.
+Newer distributions also allow downloading rpms for additional packages.</p>
+
+
+<h3><b>Activating LDAP in Chamilo</b></h3>
+
+<p class="muted">Note: The LDAP mechanism has been changed in 1.9. As a result, some of the following information might not be correct. Please check
+the configuration settings inside Chamilo to learn the details.</p>
+
+<p>
+In [Chamilo folder]/main/inc/conf/configuration.conf.php, around line 93, you'll find settings like the following: <br />
+
+// -> Uncomment the two lines below to activate LDAP AND edit main/inc/conf/auth.conf.php for configuration<br />
+// $extAuthSource["extldap"]["login"] = $_configuration['root_sys'].$_configuration['code_append']."auth/external_login/login.ldap.php";<br />
+// $extAuthSource["extldap"]["newUser"] = $_configuration['root_sys'].$_configuration['code_append']."auth/external_login/newUser.ldap.php";<br />
+<br />
+
+remove the // from the last two lines to activate LDAP.<br />
+</p>
+
+
+<h3><b>Settings</b></h3>
+
+<p>
+Ask the LDAP server admin for the settings:</p>
+
+<ul>
+
+  <li>ldap server name</li>
+
+  <li>ldap server port (usually 389)</li>
+
+  <li>ldap dc</li>
+</ul>
+Since 1.8.5, you have to change the LDAP settings inside the "Portal
+administration" panel, under "Chamilo configuration settings", section
+"LDAP".
+<br />
+
+As an example, you should find the following kind of values:<br />
+
+LDAP main server's address: "myldapserver.com";  // your ldap server<br />
+
+LDAP main server's port: 389;                 // your ldap server's port number<br />
+
+LDAP domain: "dc=xx, dc=yy, dc=zz"; //domain<br />
+
+<br />
+
+
+<h3><b>Teacher/student status</b></h3>
+
+<p>
+By default, Chamilo will check if the "employeenumber" field has a value. If it has, then Chamilo will
+consider this user as being a teacher.<br />
+
+If you want to change this behaviour, you can edit main/auth/ldap/authldap.php, function ldap_put_user_info_locally(),
+and change the <em>if (empty($info_array[$tutor_field]))</em> condition to whatever suits you.<br />
+
+You can also remove this check by removing the condition and leaving only the <em>$status = STUDENT;</em> line.</p>
+
+
+<h3><b>Protected LDAP servers</b></h3>
+
+
+<p>
+Some LDAP servers do not support anonymous use of the directory services.<br />
+In this case, you should fill in the appropriate fields in the
+administration panel (e.g. "manager" and "mypassword") and Chamilo will
+try to authenticate using these, or fall back to anonymous mode before
+giving up.</p>
+
+
+<h3>LDAP import into sessions</h3>
+
+<p>There is a new set of scripts now that allow you to insert users
+from LDAP directly into a Chamilo session. This, however, relies on a
+set of static choices in the LDAP contact attributes.<br />
+
+The fields used intensively by the Chamlio module are:<br />
+</p>
+
+<ul>
+
+    <li>uid, which is matched to the username in Chamilo</li>
+
+    <li>userPassword, which is matched to the user password, although
+this part will only work for non-encrypted passwords for now, but it
+shouldn't be necessary if using the LDAP server as authentication</li>
+
+    <li>ou should end with the year of the person registration or any
+criteria you will use to filter users, so that they can be retrieved on
+that criteria</li>
+
+    <li>sn is used as the lastname field in Chamilo</li>
+
+    <li>givenName is used as the firstname field in Chamilo</li>
+
+    <li>mail is used as the email field in Chamilo</li>
+</ul>
+
+
+<hr style="width: 100%; height: 2px;" />
+<h2><a name="7._Mathematical_formulas"></a>7. Mathematical formulas with LaTeX</h2>
+
+<i>This part is optional, only organisations planning to use mathematical formulas inside the online editor might want to read this.</i><br /><br />
+
+You can enable mathematical equations writing inside the Chamilo online editor (FCKEditor) by applying the following steps:
+<ul>
+    <li>1. Configure your Apache installation to add a cgi-bin directory that contains a symbolic link to the mimetex.cgi in <i>chamilo/main/inc/lib/mimetex/</i> (*see below, step 4)</li>
+    <li>2. Reload your Apache configuration</li>
+    <li>3. Edit the online editor's configuration file <i>chamilo/main/inc/lib/fckeditor/myconfig.php</i> and</li>
+    <ul>
+    <li>3.1. Enable the mimetex plugin, find the line //$config['LoadPlugin'][] = 'mimetex'; and modify it to be: <b>$config['LoadPlugin'][] = 'mimetex';</b> (remove the double slash)</li>
+    <li>3.2. The additional settings <b>$config['MimetexExecutableInstalled']</b> , <b>$config['MimetexExecutableDetectionMethod']</b>
+and <b>$config['MimetexExecutableDetectionTimeout']</b> are configured for best probability of automatic detection of the installed on the server file <i>mimetex.cgi</i> or <i>mimetex.exe</i>.
+In rare cases these options might need to be twicked, see the acccompanying comments about these options within the file <i>myconfig.php</i></li>
+
+    <li>3.3. Once the plugin has been activated, almost in all toolbars the corresponding button appears.
+If you need to edit some toolbars, see the configuration files (php) within the directory <i>chamilo/main/inc/lib/fckeditor/toolbars/</i> .
+Here is an example:
+    <div class="code">$config['ToolbarSets']['Normal'] = array(<br /> &nbsp;&nbsp;array('FitWindow','Bold','Image','Link','PasteWord','MP3',<b>'mimetex'</b>,'Table','Subscript','Superscript','ShowBlocks')<br />);</div>
+    The item <b>'mimetex'</b> represents the button, you may add it to or remove it from any toolbar.<br /><br />
+    </li>
+    </ul>
+    <li>4. To install mimetex executable file, make these changes:
+		<p>Add the corresponding cgi-bin directory to your Apache configuration could be done, in Apache 2, like this:</p>
+		<div class="code">
+		      ScriptAlias /cgi-bin/ /var/www/cgi-bin/<br />
+		&lt;Directory "/var/www/cgi-bin"&gt;<br />
+		&nbsp;&nbsp;AllowOverride None<br />
+		&nbsp;&nbsp;Options ExecCGI -MultiViews +SymLinksIfOwnerMatch<br />
+		&nbsp;&nbsp;Order allow,deny<br />
+		&nbsp;&nbsp;Allow from all<br />
+		&lt;/Directory&gt;<br />
+		</div>
+
+
+		<p>Adding a symbolic link can be done, under Windows, by creating a
+		  shortcut to the mimetex.exe file from the cgi-bin directory, or under
+		  Linux by issuing the following command:</p>
+		<div class="code">
+		  <p>ln -s /var/www/chamilo/main/inc/lib/mimetex/mimetex.cgi /var/www/cgi-bin/mimetex.cgi</p>
+		</div>
+		<p>If you are reluctant to modify your Apache configuration, alternately you may do at step 4 the following:
+		copy mimetex.exe (for windows) or mimetex.cgi (for linux) from <i>chamilo/main/inc/lib/mimetex/</i> directory to your <i>cgi-bin/</i></p>
+
+    </li>
+
+
+	<li>5. Make sure that the file mimetex.cgi (or mimetex.exe) has right to be executed by the web-server process. For example, on a Ubuntu Linux system you may write a command like the following:
+	    <div class="code">
+	        sudo chmod a+x mimetex.cgi
+	    </div>
+	</li>
+	<li>6. Make sure that the file mimetex.cgi (or mimetex.exe) has been uploaded in <b>BINARY</b> format.
+
+	       This is another common problem; the fix is just to re-upload the file in ASCII format.
+	       You'll need to consult your FTP program's documentation to figure out how to switch modes.
+           BINARY mode is used for non-text items, such as executables (*.exe), zip files (*.zip), image files (*.jpg, *.gif) and the like.
+           ASCII mode needs to be used for text only documents, which includes *.txt, *.cgi, *.pl *.css, *.html, etc.
+           <br />
+           You can also try to re upload the mimetex file using the "Upload file" in the "Filemanager" tool in CPanel/Plesk
+    </li>
+
+</ul>
+
+<p>As a result of this procedure, a new button becomes available in your Chamilo online editor, that gives you possibility
+to insert mathematical formulas into your documents.</p>
+<hr style="width: 100%; height: 2px;" />
+<h2><a name="8._ASCIIMathML_mathematical_formulas"></a>8. Mathematical formulas with ASCIIMathML</h2>
+
+<p>Mathematical formulas may be rendered on web-pages using the script ASCIIMathML.js (a customized version for Chamilo). For more information about this script
+and about the ASCIIMath formula syntax see <a href="http://www1.chapman.edu/~jipsen/mathml/asciimath.html">http://www1.chapman.edu/~jipsen/mathml/asciimath.html</a>
+and <a href="http://dlippman.imathas.com/asciimathtex/AMT.html">http://dlippman.imathas.com/asciimathtex/AMT.html</a>.</p>
+
+<p>For writing ASCIIMath formulas in documents, a correspondent plugin for the online editor should be activated. For doing this,
+see the platform administration section and open the page "Administration &gt; Configuration settings &gt; Editor".
+Enable the setting <strong>"ASCIIMathML mathematical editor"</strong>.</p>
+
+<p>Inside documents, the script ASCIIMathML.js renders mathematical formulas in two altrnative ways:</p>
+<ul>
+    <li>By translation ASCIIMath formula notation into MathML code. MathML standard currently is supported by the following browsers:
+        <ul>
+            <li>Mozilla Firefox, you may consider installing STIX fonts (<a href="http://www.stixfonts.org">http://www.stixfonts.org</a>) on your client machines for nice looking formulas</li>
+            <li>Internet Explorer 6 or higher with the add-on MathPlayer 2.0 or higher (<a href="http://www.dessci.com/en/products/mathplayer">http://www.dessci.com/en/products/mathplayer</a>)</li>
+            <li>Opera 9.5 or higher</li>
+        </ul>
+    </li>
+    <li>For browser that do not support MathML - by translation ASCIIMath formula notation into TeX notation and passing it to
+    an external service. The external service produces and returns an image that contains the formula. This way is so called
+    "image-based fallback".
+    </li>
+</ul>
+
+<p>For providing <strong>image-based fallback</strong> in a production system, you should pick up and install on your server software for TeX rendering, such as:</p>
+<ul>
+    <li>mimeTeX - <a href="http://www.forkosh.dreamhost.com/source_mimetex.html">http://www.forkosh.dreamhost.com/source_mimetex.html</a>.
+    See <a href="#7._Mathematical_formulas">"7. Mathematical formulas with LaTeX"</a> on how to install the mimetex executable file on your server</li>
+    <li>mathTeX - <a href="http://www.forkosh.com/mathtex.html">http://www.forkosh.com/mathtex.html</a>. Browse the site for installation instructions</li>
+</ul>
+
+<p>As an alternative, you may try to use some public services for TeX rendering, such as:</p>
+<ul>
+    <li>MathTran - <a href="http://www.mathtran.org">http://www.mathtran.org</a></li>
+    <li>Google Chart Tools - <a href="http://code.google.com/apis/charttools">http://code.google.com/apis/charttools</a></li>
+</ul>
+
+<p>Open with a text editor the file <strong>.../chamilo/main/inc/lib/asciimath/ASCIIMathML.js</strong>.
+Find somewhere at the beginning the line that initializes the variable <strong>AMTcgiloc</strong>.
+You may need to alter the setting to be for example:</p>
+<ul>
+    <li><div class="code">var AMTcgiloc = "http://mychamiloserver.org/cgi-bin/mimetex.cgi";</div></li>
+    <li><div class="code">var AMTcgiloc = "http://mychamiloserver.org/cgi-bin/mathtex.cgi";</div></li>
+    <li><div class="code">var AMTcgiloc = "http://www.mathtran.org/cgi-bin/mathtran?tex=";</div></li>
+    <li><div class="code">var AMTcgiloc = "http://chart.googleapis.com/chart?cht=tx&amp;chs=1x0&amp;chl=";</div></li>
+    <li>... or something else</li>
+</ul>
+<p>For testing how the variable <strong>AMTcgiloc</strong> may be set, there are some examples (as comments) inside the script.</p>
+
+<hr style="width: 100%; height: 2px;" />
+<h2><a name="9._WIRIS_mathematical_formulas"></a>9. Mathematical formulas with WIRIS</h2>
+<p>Installing this plugin you get WIRIS editor and WIRIS CAS. <br/>
+This activation will not be completed unless you have previously downloaded the <a href="http://www.wiris.com/plugins/editors/download?filter=fckeditor" target="_blank">PHP plugin for FCKeditor WIRIS</a> and unzipped its contents into the main/inc/lib/fckeditor/editor/plugins/fckeditor_wiris/ directory. <br/>This is necessary because Wiris is proprietary software and its services are <a href="http://www.wiris.com/store/who-pays" target="_blank">commercial</a>. To make adjustments to the plugin, edit configuration.ini file or replace his content by configuration.ini.default Chamilo file.</p>
+
+<hr style="width: 100%; height: 2px;" />
+<h2><a name="10._Xapian_fulltext_indexing"></a>Full-text indexation with Xapian</h2>
+<p>
+<em>Note</em>: This step will require a dedicated server or a virtual dedicated server as the packages involved are not available on most shared hosting solutions.<br />
+On Debian or Ubuntu 10.04 and superior, you will simply need to install the php5-xapian package and restart your web server:
+<pre>
+sudo apt-get install php5-xapian
+sudo /etc/init.d/apache2 restart
+</pre>
+
+Then go to your administration page -&gt; Configuration settings -&gt; Search
+and enable the search tool. Follow the recommendations on the page to get the
+complete indexing suite installed. Once you're done, all documents you import
+into your Chamilo portal in a recognized format will be indexed and searchable.
+Chamilo Administrators training (which you can ask any <a href="http://www.chamilo.org/en/providers">Chamilo's Official
+Provider</a> for) include a full review of the full-text search feature.
+</p>
+
+<hr style="width: 100%; height: 2px;" />
+<h2><a name="11._Chamilo_rapid_ppt_conversion"></a>Chamilo Rapid - PPT conversion system</h2>
+<p>
+<em>Note</em>: This step will require a dedicated server or a virtual dedicated server as the packages involved are not available on most shared hosting solutions.<br />
+On Debian or Ubuntu 11.10 and superior, install LibreOffice.org v3 and start it as a headless server:
+<pre>
+sudo apt-get install libreoffice
+sudo soffice -accept="socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;" -headless -nodefault -nofirststartwizard -nolockcheck -nologo -norestore &amp;
+</pre>
+Then go to your administration page -&gt; <i>Chamilo Rapid</i> and set the host to "localhost" and the port to "2002". Save. Go to your course, learning path tool and see the new icon appeared. Import your PPT. This should work.
+<em>Note</em>: Sometimes, this doesn't work out so easily. You can probably ask for the assistance of any system administrator around with a bit of Java and PHP experience, or you can always ask one of the Chamilo's Official Providers for assistance (ask for a guaranteed commercial contract).
+</p>
+
+<hr style="width: 100%; height: 2px;" />
+<h2><a name="12._Setting_cron_up"></a>Setting chronological tasks</h2>
+<p>
+Since Chamilo 1.8.8, a few tasks need to be executed regularly in order to get
+the best out of your server resources. One of such tasks (and the only one
+around at the time of Chamilo 1.8.8) is the sending of internal messaging
+notifications by e-mail, i.e. when you receive an e-mail from another person or
+from a group inside the internal messaging system of Chamilo, if all e-mails are
+sent immediately, then you might not have the chance to receive them at your
+pace, once a day or even once a week. For cases like this you, as a Chamilo
+administrator, should setup a cron process on the server to check the queue of
+e-mails and send is timely.</p>
+<p>
+Setting up a cron task is easy and there are several ways to do it. We recommend
+you have a look at the <a href="http://drupal.org/cron">Drupal documentation
+for setting up cron</a> and define your own cron process as:
+<pre>
+35 * * * * wget -O - -q -t 1 http://campus.example.com/main/cron/run.php
+</pre>
+Make sure you have a look into run.php as maybe you want to change a few
+settings there.
+</p>
+
+<hr style="width: 100%; height: 2px;" />
+<h2><a name="13._Changing_language_names_order"></a>Changing the language's firstname/lastname order</h2>
+<p>
+As Chamilo becomes more popular and crosses many borders now, it frequently happens that administrators want to re-order the firstname and lastname fields in tables, and also on which field it is sorted first.<br/><br />
+This can easily be modified by editing the main/inc/lib/internationalization_database/name_order_conventions.php file, finding your language and changing the fields. It is pretty self-explanatory and looks like this:<br />
+<pre>'simpl_chinese' => array( 'format' => 'title last_name first_name', 'sort_by' => 'last_name' ), // Eastern order</pre><br />
+Feel free to change this to<br />
+<pre>'simpl_chinese' => array( 'format' => 'title first_name lastname', 'sort_by' => 'last_name' ), // Eastern order</pre><br />
+for example. The effect should be immediate.
+</p>
+<hr style="width: 100%; height: 2px;" />
+<h2><a name="14._Improving_files_download"></a>Improving files download efficiency</h2>
+<p>
+File download can be very slow when passing through a PHP script to control permissions. One solution to this
+    is to use the X-Sendfile header, which depends on a module on the webserver. <a href="http://stackoverflow.com/a/3731639/1406662">Check http://stackoverflow.com/a/3731639/1406662 for more details on implementing Sendfile</a>.
+Chamilo LMS 1.9.8 (and following versions) supports the X-Sendfile headers, but requires a specific line of configuration to be
+    added to configuration.php:
+<pre>
+$_configuration['enable_x_sendfile_headers'] = true;
+</pre>
+If you have issues with files taking a long time to download, make sure you reconfigure your webserver and add this line. You should see an notable difference in download time.
+</p>
+<hr style="width: 100%; height: 2px;" />
+<p>
+  <br />
+  <br />
+  Adresse de contact<br />
+  Mail: info@chamilo.org<br />
+  <br />
+</p>
+<hr />
+
+</div>
+
+<hr />
+    <a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10-blue" alt="Valid XHTML 1.0 Transitional" style="margin: 1em; float: right;" height="31" width="88" /></a>
+    <a href="http://jigsaw.w3.org/css-validator/">
+        <img src="http://jigsaw.w3.org/css-validator/images/vcss-blue" style="margin: 1em; float: right;" alt="Valid CSS" />
+    </a>
+</body>
+</html>

+ 92 - 1
documentation/license.html

@@ -40,7 +40,7 @@
 <h2><b>Contents</b></h2>
 
 <ol>
-  <li><a href="#1.Using-XCache">Using xCache, APC or Memcache</a></li>
+  <li><a href="#1.Using-XCache">Using xCache, APC, Memcache or Memcached</a></li>
   <li><a href="#2.Slow-queries">Slow queries</a></li>
   <li><a href="#3.Indexes-caching">Indexes caching</a></li>
   <li><a href="#4.Sessions-directories">Sessions directories</a></li>
@@ -49,6 +49,7 @@
   <li><a href="#7.High-numbers-memory">Memory considerations for high numbers of users</a></li>
   <li><a href="#8.Avoid-non-fixed-values">Avoiding non-fixed values</a></li>
   <li><a href="#9.xsendfile">Speeding file downloads with mod_xsendfile</a></li>
+  <li><a href="#10.igbinary">IGBinary for faster courses backups and better sessions</a></li>
 </ol>
 
 <h2><a name="1.Using-XCache"></a>1. Using xCache or APC</h2>
@@ -173,6 +174,55 @@ If you prefer using <a href="http://php.net/manual/en/book.apc.php">APC</a>, you
         }
      ...
 </pre>
+<br />
+If you use php5-memcached (different set of functions than php5-memcache!), then this piece of code would look like this (you need to adjust depending on your settings):
+<pre>
+    global $_configuration;
+    $_course    = api_get_course_info();
+    $course_id  = api_get_course_id();
+    $user_id    = api_get_user_id();
+
+    $html = '';
+    $xc = method_exists('Memcached', 'add');
+    if ($xc) {
+        // Make sure the server is available
+        $xm = new Memcached;
+        $xm->addServer('localhost', 11211);
+        // The following concatenates the name of the database + the id of the
+        // access url to make it a unique variable prefix for the variables to
+        // be stored
+        $xs = $_configuration['main_database'].'_'.$_configuration['access_url'].'_';
+    }
+    $number = 0;
+    if ((api_get_setting('showonline', 'world') == 'true' AND !$user_id) OR (api_get_setting('showonline', 'users') == 'true' AND $user_id) OR (api_get_setting('showonline', 'course') == 'true' AND $user_id AND $course_id)) {
+        if ($xc) {
+            if ($xm->get($xs.'wio_count_simple')) {
+                $number = $xm->get($xs.'wio_count_simple');
+            } else {
+                $number = who_is_online_count(api_get_setting('time_limit_whosonline'));
+                $xm->set($xs.'wio_count_simple',$number,120);
+            }
+        } else {
+              $number = who_is_online_count(api_get_setting('time_limit_whosonline'));
+        }
+        $number_online_in_course = 0;
+        if (!empty($_course['id'])) {
+            if ($xc) {
+                if ($xm->get($xs.'wio_count_simple_'.$_course['id'])) {
+                    $number_online_in_course = $xm->get($xs.'wio_count_simple_'.$_course['id']);
+                } else {
+                    $number_online_in_course = who_is_online_in_this_course_count($user_id, api_get_setting('time_limit_whosonline'), $_course['id']);
+                    $xm->set($xs.'wio_count_simple_'.$_course['id'],$number_online_in_course,120);
+                }
+            } else {
+                $number_online_in_course = who_is_online_in_this_course_count(api_get_user_id(), api_get_setting('time_limit_whosonline'), $_course['id']);
+            }
+        }
+        // ...
+</pre>
+
+
+
 <p>It is also worth noting that the Université de Genève, Switzerland, observed that the calculation of the total size used by course documents is one of the heaviest queries in Chamilo, so you might want to cache the results of this one as well, using the same technique.</p>
 <p>Finally, if your portal is highly public *and* you are showing the popular courses on the homepage, you might want to also reduce the amount of queries this generates, using the same technique as above, but for the main/inc/lib/auth.lib.php library, looking for the "Tracking::get_course_connections_count()" call:</p>
 <pre>
@@ -195,6 +245,38 @@ If you prefer using <a href="http://php.net/manual/en/book.apc.php">APC</a>, you
             ...
         }
 </pre>
+Finally, the Free Campus of Chamilo has a very specific case of slow query: the courses catalog! Because there might be more than 30,000 courses in there, getting the number of "Connections last month" can be a desastrous query in terms of performances. This is why you should try to cache the results as well.<br />
+Obviously, as we are speaking about showing the number of visits this month, it doesn't really matter if the number doesn't refresh for an hour or so...<br />
+Locate the main/inc/lib/course_category.lib.php file, open it and go to the browseCoursesInCategory() function.<br />
+Locate the $count_connections_last_month = Tracking::get_course_connections_count(...) call, and wrap in into something like this:
+<pre>
+    $xc = method_exists('Memcached', 'add');
+    if ($xc) {
+        // Make sure the server is available
+        $xm = new Memcached;
+        $xm->addServer('localhost', 11211);
+        // The following concatenates the name of the database + the id of the
+        // access url to make it a unique variable prefix for the variables to
+        // be stored
+        $xs = $_configuration['main_database'].'_'.$_configuration['access_url'].'_';
+    }
+    $result = Database::query($sql);
+    $courses = array();
+    while ($row = Database::fetch_array($result)) {
+        $row['registration_code'] = !empty($row['registration_code']);
+        $count_users = CourseManager::get_users_count_in_course($row['code']);
+        if ($xc) {
+            if ($xm->get($xs.'cccount_'.$row['code'])) {
+                $number = $xm->get($xs.'cccount_'.$row['code']);
+            } else {
+                $count_connections_last_month = Tracking::get_course_connections_count($row['code'], 0, api_get_utc_datetime(time() - (30 * 86400)));
+                $xm->set($xs.'cccount_'.$row['code'], $count_connections_last_month, 3600);
+            }
+        } else {
+            $count_connections_last_month = Tracking::get_course_connections_count($row['code'], 0, api_get_utc_datetime(time() - (30 * 86400)));
+        }
+   ...
+</pre>
 <hr />
 <h2><a name="2.Slow-queries"></a>2. Slow queries</h2>
 Enable slow_queries in /etc/mysqld/my.cnf, restart MySQL then follow using sudo tail -f /var/log/mysql/mysql-slow.log
@@ -204,6 +286,11 @@ To solve this performance issue, you can execute the following queries manually
 <pre>
 ALTER TABLE lp_item ADD INDEX idx_c_lp_item_cid_lp_id (c_id, lp_id);
 ALTER TABLE lp_item_view ADD INDEX  idx_c_lp_item_view_cid_lp_view_id_lp_item_id (c_id, lp_view_id, lp_item_id);
+ALTER TABLE user_rel_tag ADD INDEX idx_user_rel_tag_user (user_id);
+</pre>
+In Chamilo 1.9.8, we use the c_item_property table more actively. This causes issues with the reporting pages for the assignments. You can reduce the impact by adding the following index:
+<pre>
+alter table c_item_property add index idx_itemprop_tooliuid(tool, insert_user_id);
 </pre>
 These will be available in Chamilo 1.10 directly, but we cannot put them into Chamilo 1.9 from now on for organizational reasons.<br />
 <hr />
@@ -310,6 +397,10 @@ $_configuration['enable_x_sendfile_headers'] = true;
 </pre>
 Done! Now your downloads should go substantially faster. This is still a feature in observation. We're not sure the benefits are sufficient, so don't hesitate to let us know in <a href="https://support.chamilo.org/issues/6853">the related issue in Chamilo's tracking system</a>
 </p>
+<h2><a name="#10.igbinary"></a>IGBinary for courses backups and better sessions management</h2>
+<p>
+<a href="http://pecl.php.net/package/igbinary">IGBinary</a> is a small PECL library that replaces the PHP serializer. It uses less space (so less memory for serialized objects) and is particularly efficient with memory-based storages (like Memcached). Use it for course backups (see <a href="https://support.chamilo.org/issues/4443">issue 4443</a>) or <a href="http://www.neanderthal-technology.com/2011/11/ubuntu-10-install-php-memcached-with-igbinary-support/">to boost sessions management</a>.
+</p>
 <hr />
 <h2>Authors</h2>
 <ul>

+ 5 - 2
documentation/readme.html

@@ -179,6 +179,10 @@ if (api_is_platform_admin() || api_is_drh()) {
     $controller->tpl->assign('skills_block', $controller->return_skills_links());
 }
 
+if (api_is_anonymous()) {
+    $controller->tpl->setLoginBodyClass();
+}
+
 // direct login to course
 if (isset($_GET['firstpage'])) {
     api_set_firstpage_parameter($_GET['firstpage']);
@@ -186,8 +190,7 @@ if (isset($_GET['firstpage'])) {
     if (api_user_is_login()) {
         echo "<script type='text/javascript'>self.location.href='index.php?firstpage=".$_GET['firstpage']."'</script>";
     }
-}
-else {
+} else {
     api_delete_firstpage_parameter();
 }
 

+ 1 - 0
license.txt

@@ -17,6 +17,7 @@ require_once api_get_path(LIBRARY_PATH).'urlmanager.lib.php';
 $this_section=SECTION_PLATFORM_ADMIN;
 
 api_protect_global_admin_script();
+
 if (!api_get_multiple_access_url()) {
     header('Location: index.php');
     exit;

+ 3 - 3
main/admin/access_url_add_users_to_url.php

@@ -12,13 +12,13 @@
 $language_file = 'admin';
 $cidReset = true;
 require_once '../inc/global.inc.php';
-$this_section=SECTION_PLATFORM_ADMIN;
+$this_section = SECTION_PLATFORM_ADMIN;
 
 require_once api_get_path(LIBRARY_PATH).'urlmanager.lib.php';
 api_protect_global_admin_script();
 if (!api_get_multiple_access_url()) {
-    header('Location: index.php');
-    exit;
+	header('Location: index.php');
+	exit;
 }
 
 $form_sent = 0;

+ 10 - 10
main/admin/access_url_check_user_session.php

@@ -34,14 +34,14 @@ $url_id                             = api_get_current_access_url_id();
 $action = $_GET['action'];
 
 switch($action) {
-    case 'add_user_to_url':        
+    case 'add_user_to_url':
         $user_id = $_REQUEST['user_id'];
         $result = UrlManager::add_user_to_url($user_id, $url_id);
         $user_info = api_get_user_info($user_id);
         if ($result) {
             $message = Display::return_message(get_lang('UserAdded').' '.api_get_person_name($user_info['firstname'], $user_info['lastname']), 'confirm');
         }
-        break;    
+        break;
 }
 
 Display::display_header($tool_name);
@@ -69,7 +69,7 @@ if ($show_users_with_problems) {
 foreach($session_list  as $session_item) {
     $session_id = $session_item['id'];
     $html .= '<h3>'.$session_item['name'].'</h3>';
-    
+
     $access_where = "(access_url_id = $url_id OR access_url_id is null )";
     if ($show_users_with_problems) {
         $access_where = "(access_url_id is null)";
@@ -85,24 +85,24 @@ foreach($session_list  as $session_item) {
 
     $result = Database::query($sql);
     $users  = Database::store_result($result);
-    
+
     if (!empty($users)) {
-        $html .= '<table class="data_table"><tr><th>'.get_lang('User').'<th>'.get_lang('Actions').'</th></tr>';   
-    
+        $html .= '<table class="data_table"><tr><th>'.get_lang('User').'<th>'.get_lang('Actions').'</th></tr>';
+
         foreach ($users as $user) {
             $user_link = '';
             if (!empty($user['user_id'])) {
-                $user_link = '<a href="'.api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.intval($user['user_id']).'">'.api_htmlentities(api_get_person_name($user['firstname'], $user['lastname']),ENT_QUOTES,$charset).' ('.$user['username'].')</a>';
+                $user_link = '<a href="'.api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.intval($user['user_id']).'">'.Security::remove_XSS(api_get_person_name($user['firstname'], $user['lastname'])).' ('.$user['username'].')</a>';
             }
 
             $link_to_add_user_in_url = '';
 
             if ($multiple_url_is_on) {
-                if ($user['access_url_id'] != $url_id) {            
+                if ($user['access_url_id'] != $url_id) {
                     $user_link .= ' '.Display::return_icon('warning.png', get_lang('UserNotAddedInURL'), array(), ICON_SIZE_MEDIUM);
-                    $add = Display::return_icon('add.png', get_lang('AddUsersToURL'), array(), ICON_SIZE_MEDIUM);                    
+                    $add = Display::return_icon('add.png', get_lang('AddUsersToURL'), array(), ICON_SIZE_MEDIUM);
                     $link_to_add_user_in_url = '<a href="'.api_get_self().'?'.Security::remove_XSS($_SERVER['QUERY_STRING']).'&action=add_user_to_url&id_session='.$id_session.'&user_id='.$user['user_id'].'">'.$add.'</a>';
-                }                
+                }
             }
             $html .= '<tr>
                     <td>

+ 5 - 4
main/admin/access_url_edit.php

@@ -111,8 +111,8 @@ $form->addRule('url', '', 'maxlength',254);
 $form->addElement('textarea','description',get_lang('Description'));
 
 //the first url with id = 1 will be always active
-if ($_GET['url_id'] != 1) {
-	$form->addElement('checkbox','active', null, get_lang('Active'));
+if (isset($_GET['url_id']) && $_GET['url_id'] != 1) {
+    $form->addElement('checkbox','active', null, get_lang('Active'));
 }
 
 $defaults['url']='http://';
@@ -132,14 +132,15 @@ if (isset($_GET['url_id'])) {
 	$submit_name = get_lang('AddUrl');
 }
 
-if (!$_configuration['multiple_access_urls']) {
-	header('Location: index.php');
+if (!api_is_multiple_url_enabled()) {
+    header('Location: index.php');
     exit;
 }
 
 $tool_name = get_lang('AddUrl');
 $interbreadcrumb[] = array ("url" => 'index.php', "name" => get_lang('PlatformAdmin'));
 $interbreadcrumb[] = array ("url" => 'access_urls.php', "name" => get_lang('MultipleAccessURLs'));
+
 Display :: display_header($tool_name);
 
 if (isset ($_GET['action'])) {

+ 2 - 2
main/admin/access_url_edit_course_category_to_url.php

@@ -187,7 +187,7 @@ if (!empty($errorMsg)) {
   <td align="center"><b><?php echo get_lang('CourseCategoryInPlatform') ?> :</b>
   </td>
   <td></td>
-  <td align="center"><b><?php echo get_lang('CourseCategoryListIn').' '.$url_selected; ?></b></td>
+  <td align="center"><b><?php printf(get_lang('CourseCategoryListInX'),$url_selected); ?></b></td>
 </tr>
 
 <tr>
@@ -334,4 +334,4 @@ function makepost(select){
 }
 </script>
 <?php
-Display::display_footer();
+Display::display_footer();

+ 7 - 13
main/admin/access_url_edit_courses_to_url.php

@@ -46,14 +46,13 @@ if(isset($_REQUEST['add_type']) && $_REQUEST['add_type']!='') {
 }
 
 $access_url_id=1;
-if(isset($_REQUEST['access_url_id']) && $_REQUEST['access_url_id']!=''){
+if (isset($_REQUEST['access_url_id']) && $_REQUEST['access_url_id']!='') {
 	$access_url_id = Security::remove_XSS($_REQUEST['access_url_id']);
 }
 
 $xajax -> processRequests();
 $htmlHeadXtra[] = $xajax->getJavascript('../inc/lib/xajax/');
-$htmlHeadXtra[] = '
-<script type="text/javascript">
+$htmlHeadXtra[] = '<script>
 function add_user_to_url(code, content) {
 
 	document.getElementById("course_to_add").value = "";
@@ -67,7 +66,7 @@ function add_user_to_url(code, content) {
 }
 
 function send() {
-	if (document.formulaire.access_url_id.value!=0) {
+	if (document.formulaire.access_url_id.value != 0) {
 		document.formulaire.form_sent.value=0;
 		document.formulaire.add_type.value=\''.$add_type.'\';
 		document.formulaire.submit();
@@ -93,16 +92,16 @@ if (isset($_POST['form_sent']) && $_POST['form_sent']) {
 	$form_sent=$_POST['form_sent'];
 	$course_list=$_POST['course_list'];
 
-	if(!is_array($course_list)) {
+	if (!is_array($course_list)) {
 		$course_list=array();
 	}
 
-	if($form_sent == 1) {
+	if ($form_sent == 1) {
 		if ($access_url_id==0) {
 			header('Location: access_url_edit_users_to_url.php?action=show_message&message='.get_lang('SelectURL'));
 		}
 		elseif(is_array($course_list) ) {
-			UrlManager::update_urls_rel_course($course_list,$access_url_id);
+			UrlManager::update_urls_rel_course($course_list, $access_url_id);
 			header('Location: access_urls.php?action=show_message&message='.get_lang('CoursesWereEdited'));
 		}
 	}
@@ -196,6 +195,7 @@ $url_list = UrlManager::get_url_data();
 if(!empty($errorMsg)) {
 	Display::display_normal_message($errorMsg); //main API
 }
+
 ?>
 
 <table border="0" cellpadding="5" cellspacing="0" width="100%">
@@ -318,11 +318,6 @@ function valide(){
 	var options = document.getElementById('destination_users').options;
 	for (i = 0 ; i<options.length ; i++)
 		options[i].selected = true;
-	/*
-	var options = document.getElementById('destination_classes').options;
-	for (i = 0 ; i<options.length ; i++)
-		options[i].selected = true;
-		*/
 	document.forms.formulaire.submit();
 }
 
@@ -362,7 +357,6 @@ function makepost(select){
 
 	return ret;
 }
--->
 </script>
 <?php
 Display::display_footer();

+ 2 - 2
main/admin/access_url_edit_users_to_url.php

@@ -24,6 +24,7 @@ $this_section = SECTION_PLATFORM_ADMIN;
 
 // Access restrictions
 api_protect_global_admin_script();
+
 if (!api_get_multiple_access_url()) {
     header('Location: index.php');
     exit;
@@ -52,8 +53,7 @@ if (isset($_REQUEST['access_url_id']) && $_REQUEST['access_url_id'] != '') {
 
 $xajax->processRequests();
 $htmlHeadXtra[] = $xajax->getJavascript('../inc/lib/xajax/');
-$htmlHeadXtra[] = '
-<script type="text/javascript">
+$htmlHeadXtra[] = '<script>
 function add_user_to_url(code, content) {
 	document.getElementById("user_to_add").value = "";
 	document.getElementById("ajax_list_users").innerHTML = "";

+ 5 - 4
main/admin/access_urls.php

@@ -1,10 +1,11 @@
 <?php
 /* For licensing terms, see /license.txt */
 /**
-*	@package chamilo.admin
-*	@author Julio Montoya <gugli100@gmail.com>
-*/
-
+ * Frontend script for multiple access urls
+ * @package chamilo.admin
+ * @author Julio Montoya <gugli100@gmail.com>
+ * @author Yannick Warnier <yannick.warnier@beeznest.com>
+ */
 // name of the language file that needs to be included
 $language_file = 'admin';
 $cidReset = true;

+ 45 - 51
main/admin/add_courses_to_session.php

@@ -20,7 +20,7 @@ $add = isset($_GET['add']) ? Security::remove_XSS($_GET['add']) : null;
 SessionManager::protect_session_edit($id_session);
 
 $xajax = new xajax();
-$xajax->registerFunction (array('search_courses', 'AddCourseToSession', 'search_courses'));
+$xajax->registerFunction(array('search_courses', 'AddCourseToSession', 'search_courses'));
 
 // Setting the section (for the tabs)
 $this_section = SECTION_PLATFORM_ADMIN;
@@ -47,29 +47,25 @@ if(isset($_GET['add_type']) && $_GET['add_type']!=''){
 
 $page = isset($_GET['page']) ? Security::remove_XSS($_GET['page']) : null;
 
-$xajax -> processRequests();
+$xajax->processRequests();
 
 $htmlHeadXtra[] = $xajax->getJavascript('../inc/lib/xajax/');
-$htmlHeadXtra[] = '
-<script type="text/javascript">
-function add_course_to_session (code, content) {
-
+$htmlHeadXtra[] = '<script>
+function add_course_to_session(code, content) {
 	document.getElementById("course_to_add").value = "";
 	document.getElementById("ajax_list_courses_single").innerHTML = "";
-
 	destination = document.getElementById("destination");
-
 	for (i=0;i<destination.length;i++) {
-		if(destination.options[i].text == content) {
-				return false;
+		if (destination.options[i].text == content) {
+            return false;
 		}
 	}
 
 	destination.options[destination.length] = new Option(content,code);
-
 	destination.selectedIndex = -1;
 	sortOptions(destination.options);
 }
+
 function remove_item(origin)
 {
 	for(var i = 0 ; i<origin.options.length ; i++) {
@@ -81,17 +77,17 @@ function remove_item(origin)
 }
 </script>';
 
-$formSent=0;
-$errorMsg=$firstLetterCourse=$firstLetterSession='';
-$CourseList=$SessionList=array();
-$courses=$sessions=array();
-$noPHP_SELF=true;
+$formSent = 0;
+$errorMsg = $firstLetterCourse = $firstLetterSession = '';
+$CourseList = $SessionList = array();
+$courses = $sessions = array();
+$noPHP_SELF = true;
 
 if (isset($_POST['formSent']) && $_POST['formSent']) {
-	$formSent              = $_POST['formSent'];
-	$firstLetterCourse     = $_POST['firstLetterCourse'];
-	$firstLetterSession    = $_POST['firstLetterSession'];
-	$CourseList            = $_POST['SessionCoursesList'];
+    $formSent = $_POST['formSent'];
+    $firstLetterCourse = $_POST['firstLetterCourse'];
+    $firstLetterSession = $_POST['firstLetterSession'];
+    $CourseList = $_POST['SessionCoursesList'];
 	if (!is_array($CourseList)) {
 		$CourseList = array();
 	}
@@ -105,21 +101,20 @@ if (isset($_POST['formSent']) && $_POST['formSent']) {
 	$existingCourses = Database::store_result($rs);
 
     // Updating only the RRHH users?? why?
-	//$sql="SELECT id_user FROM $tbl_session_rel_user WHERE id_session = $id_session AND relation_type=".COURSE_RELATION_TYPE_RRHH." ";
-    $sql        = "SELECT id_user FROM $tbl_session_rel_user WHERE id_session = $id_session ";
-	$result     = Database::query($sql);
-	$UserList   = Database::store_result($result);
-
+    $sql = "SELECT id_user FROM $tbl_session_rel_user WHERE id_session = $id_session ";
+    $result = Database::query($sql);
+    $UserList = Database::store_result($result);
 
-	foreach($CourseList as $enreg_course) {
+	foreach ($CourseList as $enreg_course) {
 		$enreg_course = Database::escape_string($enreg_course);
 		$exists = false;
-		foreach($existingCourses as $existingCourse) {
-			if($enreg_course == $existingCourse['course_code']) {
-				$exists=true;
-			}
-		}
-		if(!$exists) {
+        foreach ($existingCourses as $existingCourse) {
+            if ($enreg_course == $existingCourse['course_code']) {
+                $exists = true;
+            }
+        }
+
+		if (!$exists) {
 			$sql_insert_rel_course= "INSERT INTO $tbl_session_rel_course(id_session,course_code) VALUES('$id_session','$enreg_course')";
 			Database::query($sql_insert_rel_course);
 
@@ -131,7 +126,8 @@ if (isset($_POST['formSent']) && $_POST['formSent']) {
 			$nbr_users=0;
 			foreach ($UserList as $enreg_user) {
 				$enreg_user = Database::escape_string($enreg_user['id_user']);
-				$sql_insert = "INSERT IGNORE INTO $tbl_session_rel_course_rel_user(id_session,course_code,id_user) VALUES('$id_session','$enreg_course','$enreg_user')";
+				$sql_insert = "INSERT IGNORE INTO $tbl_session_rel_course_rel_user(id_session,course_code,id_user)
+                               VALUES('$id_session','$enreg_course','$enreg_user')";
 				Database::query($sql_insert);
 				if(Database::affected_rows()) {
 					$nbr_users++;
@@ -149,7 +145,6 @@ if (isset($_POST['formSent']) && $_POST['formSent']) {
 			Database::query("DELETE FROM $tbl_session_rel_course WHERE course_code='".$existingCourse['course_code']."' AND id_session=$id_session");
 			Database::query("DELETE FROM $tbl_session_rel_course_rel_user WHERE course_code='".$existingCourse['course_code']."' AND id_session=$id_session");
             SessionManager::unInstallCourse($id_session, $course_info['real_id']);
-
 		}
 	}
 	$nbr_courses=count($CourseList);
@@ -168,12 +163,12 @@ Display::display_header($tool_name);
 
 if ($add_type == 'multiple') {
 	$link_add_type_unique = '<a href="'.api_get_self().'?id_session='.$id_session.'&add='.$add.'&add_type=unique">'.
-        Display::return_icon('single.gif').get_lang('SessionAddTypeUnique').'</a>';
+    Display::return_icon('single.gif').get_lang('SessionAddTypeUnique').'</a>';
 	$link_add_type_multiple = Display::return_icon('multiple.gif').get_lang('SessionAddTypeMultiple').' ';
 } else {
 	$link_add_type_unique = Display::return_icon('single.gif').get_lang('SessionAddTypeUnique').'&nbsp;&nbsp;&nbsp;';
 	$link_add_type_multiple = '<a href="'.api_get_self().'?id_session='.$id_session.'&add='.$add.'&add_type=multiple">'.
-        Display::return_icon('multiple.gif').get_lang('SessionAddTypeMultiple').'</a>';
+    Display::return_icon('multiple.gif').get_lang('SessionAddTypeMultiple').'</a>';
 }
 
 // the form header
@@ -205,7 +200,6 @@ if ($ajax_search) {
 				INNER JOIN $tbl_course_rel_access_url url_course ON (url_course.course_code=course.code)
 				WHERE access_url_id = $access_url_id
 			ORDER BY ".(sizeof($courses)?"(code IN(".implode(',',$courses).")) DESC,":"")." title";
-
 		}
 	}
 
@@ -239,7 +233,7 @@ if ($ajax_search) {
 	}
 	$result = Database::query($sql);
 	$Courses = Database::store_result($result);
-	foreach($Courses as $course) {
+	foreach ($Courses as $course) {
 		if ($course['id_session'] == $id_session) {
 			$sessionCourses[$course['code']] = $course ;
 		} else {
@@ -254,8 +248,7 @@ unset($Courses);
 <input type="hidden" name="formSent" value="1" />
 
 <?php
-if(!empty($errorMsg))
-{
+if (!empty($errorMsg)) {
 	Display::display_normal_message($errorMsg); //main API
 }
 ?>
@@ -295,10 +288,11 @@ if (!($add_type == 'multiple')) {
 	?>
 	<div id="ajax_list_courses_multiple">
 	<select id="origin" name="NoSessionCoursesList[]" multiple="multiple" size="20" style="width:360px;"> <?php
-	foreach($nosessionCourses as $enreg)
-	{
+	foreach($nosessionCourses as $enreg) {
 		?>
-		<option value="<?php echo $enreg['code']; ?>" <?php echo 'title="'.htmlspecialchars($enreg['title'].' ('.$enreg['visual_code'].')',ENT_QUOTES).'"'; if(in_array($enreg['code'],$CourseList)) echo 'selected="selected"'; ?>><?php echo $enreg['title'].' ('.$enreg['visual_code'].')'; ?></option>
+		<option value="<?php echo $enreg['code']; ?>" <?php echo 'title="'.htmlspecialchars($enreg['title'].' ('.$enreg['visual_code'].')',ENT_QUOTES).'"'; if(in_array($enreg['code'],$CourseList)) echo 'selected="selected"'; ?>>
+            <?php echo $enreg['title'].' ('.$enreg['visual_code'].')'; ?>
+        </option>
 		<?php
 	}
 	?></select>
@@ -324,7 +318,7 @@ unset($nosessionCourses);
   ?>
 	<br /><br /><br /><br /><br /><br />
 	<?php
-	if(isset($_GET['add'])) {
+	if (isset($_GET['add'])) {
 		echo '<button class="save" type="button" value="" onclick="valide()" >'.get_lang('NextStep').'</button>';
 	} else {
 		echo '<button class="save" type="button" value="" onclick="valide()" >'.get_lang('SubscribeCoursesToSession').'</button>';
@@ -334,11 +328,11 @@ unset($nosessionCourses);
   <td width="45%" align="center"><select id='destination' name="SessionCoursesList[]" multiple="multiple" size="20" style="width:360px;">
 
 <?php
-foreach($sessionCourses as $enreg)
-{
+foreach($sessionCourses as $enreg) {
 ?>
-	<option value="<?php echo $enreg['code']; ?>" title="<?php echo htmlspecialchars($enreg['title'].' ('.$enreg['visual_code'].')',ENT_QUOTES); ?>"><?php echo $enreg['title'].' ('.$enreg['visual_code'].')'; ?></option>
-
+	<option value="<?php echo $enreg['code']; ?>" title="<?php echo htmlspecialchars($enreg['title'].' ('.$enreg['visual_code'].')',ENT_QUOTES); ?>">
+        <?php echo $enreg['title'].' ('.$enreg['visual_code'].')'; ?>
+    </option>
 <?php
 }
 unset($sessionCourses);
@@ -347,7 +341,7 @@ unset($sessionCourses);
 </tr>
 </table>
 </form>
-<script type="text/javascript">
+<script>
 function moveItem(origin , destination) {
 	for(var i = 0 ; i<origin.options.length ; i++) {
 		if(origin.options[i].selected) {
@@ -374,7 +368,7 @@ function sortOptions(options) {
 	}
 }
 
-function mysort(a, b){
+function mysort(a, b) {
 	if(a.text.toLowerCase() > b.text.toLowerCase()){
 		return 1;
 	}
@@ -384,7 +378,7 @@ function mysort(a, b){
 	return 0;
 }
 
-function valide(){
+function valide() {
 	var options = document.getElementById('destination').options;
 	for (i = 0 ; i<options.length ; i++)
 		options[i].selected = true;

+ 1 - 2
main/admin/add_courses_to_usergroup.php

@@ -39,8 +39,7 @@ if (isset($_REQUEST['add_type']) && $_REQUEST['add_type']!=''){
 $add = isset($_GET['add']) ? Security::remove_XSS($_GET['add']) : null;
 
 $htmlHeadXtra[] = $xajax->getJavascript('../inc/lib/xajax/');
-$htmlHeadXtra[] = '
-<script>
+$htmlHeadXtra[] = '<script>
 function add_user_to_session (code, content) {
     document.getElementById("user_to_add").value = "";
     document.getElementById("ajax_list_users_single").innerHTML = "";

+ 44 - 43
main/admin/add_many_session_to_category.php

@@ -71,7 +71,6 @@ function add_course_to_session (code, content) {
 }
 function send() {
 	if (document.formulaire.CategorySessionId.value!=0) {
-		//alert(document.formulaire.CategorySessionId.value);
 		document.formulaire.formSent.value=0;
 		document.formulaire.submit();
 	}
@@ -87,54 +86,52 @@ function remove_item(origin)
 }
 </script>';
 
-$formSent=0;
-$errorMsg = $firstLetterCourse = $firstLetterSession='';
-$CourseList=$SessionList=array();
-$courses=$sessions=array();
-$Categoryid = intval($_POST['CategorySessionId']);
-
-if ($_POST['formSent']) {
-	$formSent=$_POST['formSent'];
-	$SessionCategoryList = $_POST['SessionCategoryList'];
-
-	if($Categoryid != 0 && count($SessionCategoryList)>0 ){
-		$session_id = join(',', $SessionCategoryList);
-		$sql = "UPDATE $tbl_session SET session_category_id = $Categoryid WHERE id in ($session_id) ";
-		Database::query($sql);
-		//header('Location: session_list.php?id_category='.$Categoryid);
-		header('Location: add_many_session_to_category.php?id_category='.$Categoryid.'&msg=ok');
-		exit;
-	} else {
-		header('Location: add_many_session_to_category.php?msg=error');
-		exit;
-	}
+$formSent = 0;
+$errorMsg = $firstLetterCourse = $firstLetterSession = '';
+$CourseList = $SessionList = array();
+$courses = $sessions = array();
+$Categoryid = isset($_POST['CategorySessionId']) ? intval($_POST['CategorySessionId']) : null;
+
+if (isset($_POST['formSent']) && $_POST['formSent']) {
+    $formSent = $_POST['formSent'];
+    $SessionCategoryList = $_POST['SessionCategoryList'];
+
+    if ($Categoryid != 0 && count($SessionCategoryList) > 0) {
+        $session_id = join(',', $SessionCategoryList);
+        $sql = "UPDATE $tbl_session SET session_category_id = $Categoryid WHERE id in ($session_id) ";
+        Database::query($sql);
+        header('Location: add_many_session_to_category.php?id_category=' . $Categoryid . '&msg=ok');
+        exit;
+    } else {
+        header('Location: add_many_session_to_category.php?msg=error');
+        exit;
+    }
 }
 
 if (isset($_GET['id_category'])) {
-	$Categoryid = intval($_GET['id_category']);
+    $Categoryid = intval($_GET['id_category']);
 }
 
-if(isset($_GET['msg']) && $_GET['msg']=='error'){
-	$errorMsg = get_lang('MsgErrorSessionCategory');
+if (isset($_GET['msg']) && $_GET['msg'] == 'error') {
+    $errorMsg = get_lang('MsgErrorSessionCategory');
 }
 
-if(isset($_GET['msg']) && $_GET['msg']=='ok'){
-	$OkMsg = get_lang('SessionCategoryUpdate');
+if (isset($_GET['msg']) && $_GET['msg'] == 'ok') {
+    $OkMsg = get_lang('SessionCategoryUpdate');
 }
 
-
+$page = isset($_GET['page']) ? Security::remove_XSS($_GET['page']) : null;
 // display the dokeos header
 Display::display_header($tool_name);
 
-
-$where ='';
+$where = '';
 $rows_category_session = array();
-if((isset($_POST['CategorySessionId']) && $_POST['formSent'] == 0) || isset($_GET['id_category']) ) {
-	
-	$where = 'WHERE session_category_id !='.$Categoryid;
-	$sql = 'SELECT id, name  FROM '.$tbl_session .' WHERE session_category_id ='.$Categoryid.' ORDER BY name';
-	$result=Database::query($sql);
-	$rows_category_session = Database::store_result($result);
+if ((isset($_POST['CategorySessionId']) && $_POST['formSent'] == 0) || isset($_GET['id_category'])) {
+
+    $where = 'WHERE session_category_id !=' . $Categoryid;
+    $sql = 'SELECT id, name  FROM ' . $tbl_session . ' WHERE session_category_id =' . $Categoryid . ' ORDER BY name';
+    $result = Database::query($sql);
+    $rows_category_session = Database::store_result($result);
 }
 
 $rows_session_category = SessionManager::get_all_session_category();
@@ -154,13 +151,17 @@ if (api_get_multiple_access_url()) {
 $result=Database::query($sql);
 $rows_session = Database::store_result($result);
 ?>
-<form name="formulaire" method="post" action="<?php echo api_get_self(); ?>?page=<?php echo Security::remove_XSS($_GET['page']); if(!empty($_GET['add'])) echo '&add=true' ; ?>" style="margin:0px;" <?php if($ajax_search){echo ' onsubmit="valide();"';}?>>
-    <?php echo '<legend>'.$tool_name.'</legend>'; ?>
-<input type="hidden" name="formSent" value="1" />
-<?php
-if(!empty($errorMsg)) {
-	Display::display_error_message($errorMsg); //main API
-}
+<form name="formulaire" method="post"
+      action="<?php echo api_get_self(); ?>?page=<?php echo $page;
+      if (!empty($_GET['add'])) {
+          echo '&add=true';
+      } ?>" style="margin:0px;">
+    <?php echo '<legend>' . $tool_name . '</legend>'; ?>
+    <input type="hidden" name="formSent" value="1"/>
+    <?php
+    if (!empty($errorMsg)) {
+        Display::display_error_message($errorMsg); //main API
+    }
 
 if(!empty($OkMsg)) {
 	Display::display_confirmation_message($OkMsg); //main API

+ 1 - 2
main/admin/add_sessions_to_promotion.php

@@ -38,8 +38,7 @@ if (isset($_REQUEST['add_type']) && $_REQUEST['add_type']!=''){
 }
 
 $htmlHeadXtra[] = $xajax->getJavascript('../inc/lib/xajax/');
-$htmlHeadXtra[] = '
-<script>
+$htmlHeadXtra[] = '<script>
 function add_user_to_session (code, content) {
 
     document.getElementById("user_to_add").value = "";

+ 19 - 24
main/admin/add_sessions_to_usergroup.php

@@ -41,8 +41,7 @@ if(isset($_REQUEST['add_type']) && $_REQUEST['add_type']!=''){
 }
 
 $htmlHeadXtra[] = $xajax->getJavascript('../inc/lib/xajax/');
-$htmlHeadXtra[] = '
-<script type="text/javascript">
+$htmlHeadXtra[] = '<script>
 function add_user_to_session (code, content) {
 
     document.getElementById("user_to_add").value = "";
@@ -80,9 +79,9 @@ function display_advanced_search () {
 }
 
 function validate_filter() {
-        document.formulaire.add_type.value = \''.$add_type.'\';
-        document.formulaire.form_sent.value=0;
-        document.formulaire.submit();
+    document.formulaire.add_type.value = \''.$add_type.'\';
+    document.formulaire.form_sent.value=0;
+    document.formulaire.submit();
 }
 </script>';
 
@@ -92,9 +91,9 @@ $errorMsg   = '';
 $sessions=array();
 $usergroup = new UserGroup();
 $id = intval($_GET['id']);
-if($_POST['form_sent']) {
-    $form_sent          = $_POST['form_sent'];    
-    $elements_posted    = $_POST['elements_in_name'];     
+if (isset($_POST['form_sent']) && $_POST['form_sent']) {
+    $form_sent          = $_POST['form_sent'];
+    $elements_posted    = $_POST['elements_in_name'];
     if (!is_array($elements_posted)) {
         $elements_posted = array();
     }
@@ -102,7 +101,7 @@ if($_POST['form_sent']) {
         //added a parameter to send emails when registering a user        
         $usergroup->subscribe_sessions_to_usergroup($id, $elements_posted);
         header('Location: usergroups.php');
-        exit;        
+        exit;
     }
 }
 $data               = $usergroup->get($id);
@@ -113,9 +112,9 @@ $session_list       = SessionManager::get_sessions_list(array(), array('name'));
 $elements_not_in = $elements_in= array();
 
 if (!empty($session_list)) {
-    foreach($session_list as $session) {        
-        if (in_array($session['id'], $session_list_in)) {            
-            $elements_in[$session['id']] = $session['name'];             
+    foreach($session_list as $session) {
+        if (in_array($session['id'], $session_list_in)) {
+            $elements_in[$session['id']] = $session['name'];
         } else {
             $elements_not_in[$session['id']] = $session['name'];
         }
@@ -130,7 +129,7 @@ function search_sessions($needle,$type) {
     global $tbl_user,$elements_in;
     $xajax_response = new XajaxResponse();
     $return = '';
-    if (isset($needle) && !empty($type)) {
+    if (!empty($needle) && !empty($type)) {
 
         // xajax send utf8 datas... datas in db can be non-utf8 datas
         $charset = api_get_system_encoding();
@@ -147,11 +146,10 @@ function search_sessions($needle,$type) {
                 ' LIMIT 11';*/
         } else if ($type == 'searchbox') {
             $session_list = SessionManager::get_sessions_list(array('s.name LIKE' => "%$needle%"));
-        }
-        else {
+        } else {
             $session_list = SessionManager::get_sessions_list(array('s.name LIKE' => "$needle%"));
         }
-        $i=0;        
+        $i=0;
         if ($type=='single') {
             /*
             while ($user = Database :: fetch_array($rs)) {
@@ -166,9 +164,9 @@ function search_sessions($needle,$type) {
             $xajax_response -> addAssign('ajax_list_users_single','innerHTML',api_utf8_encode($return));*/
         } else {
             $return .= '<select id="elements_not_in" name="elements_not_in_name[]" multiple="multiple" size="15" style="width:360px;">';
-            
-            foreach ($session_list as $row ) {         
-                if (!in_array($row['id'], array_keys($elements_in))) {       
+
+            foreach ($session_list as $row ) {
+                if (!in_array($row['id'], array_keys($elements_in))) {
                     $return .= '<option value="'.$row['id'].'">'.$row['name'].'</option>';
                 }
             }
@@ -270,10 +268,10 @@ if(!empty($errorMsg)) {
         <input type="text" id="user_to_add" onkeyup="xajax_search_users(this.value,'single')" />
         <div id="ajax_list_users_single"></div>
         <?php
-      } else {               
+      } else {
       ?>
       <div id="ajax_list_multiple">
-        <?php echo Display::select('elements_not_in_name',$elements_not_in, '',array('style'=>'width:360px', 'multiple'=>'multiple','id'=>'elements_not_in','size'=>'15px'),false); ?> 
+        <?php echo Display::select('elements_not_in_name',$elements_not_in, '',array('style'=>'width:360px', 'multiple'=>'multiple','id'=>'elements_not_in','size'=>'15px'),false); ?>
       </div>
     <?php
       }
@@ -360,7 +358,6 @@ function valide(){
     document.forms.formulaire.submit();
 }
 
-
 function loadUsersInSelect(select){
 
     var xhr_object = null;
@@ -372,7 +369,6 @@ function loadUsersInSelect(select){
     else  // XMLHttpRequest non supporté par le navigateur
     alert("Votre navigateur ne supporte pas les objets XMLHTTPRequest...");
 
-    //xhr_object.open("GET", "loadUsersInSelect.ajax.php?id_session=<?php echo $id_session ?>&letter="+select.options[select.selectedIndex].text, false);
     xhr_object.open("POST", "loadUsersInSelect.ajax.php");
 
     xhr_object.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
@@ -400,7 +396,6 @@ function makepost(select){
 
     return ret;
 }
--->
 </script>
 <?php
 Display::display_footer();

+ 0 - 4
main/admin/add_users_to_group.php

@@ -182,10 +182,6 @@ $htmlHeadXtra[] = $xajax->getJavascript('../inc/lib/xajax/');
 $htmlHeadXtra[] = '
 <script>
 function add_user (code, content) {
-
-	// document.getElementById("user_to_add").value = "";
-	//document.getElementById("ajax_list_users_single").innerHTML = "";
-
 	destination = document.getElementById("destination_users");
 	for (i=0;i<destination.length;i++) {
 		if (destination.options[i].text == content) {

+ 25 - 4
main/admin/add_users_to_usergroup.php

@@ -87,7 +87,7 @@ function change_select(val) {
 $form_sent  = 0;
 $errorMsg   = '';
 
-$extra_field_list= UserManager::get_extra_fields();
+$extra_field_list = UserManager::get_extra_fields();
 $new_field_list = array();
 if (is_array($extra_field_list)) {
     foreach ($extra_field_list as $extra_field) {
@@ -125,6 +125,24 @@ if (isset($_POST['form_sent']) && $_POST['form_sent']) {
     }
 }
 
+if (isset($_GET['action']) && $_GET['action'] == 'export') {
+    $groupInfo = $usergroup->get($id);
+    $users = $usergroup->getUserListByUserGroup($id);
+    if (!empty($users)) {
+        require_once api_get_path(LIBRARY_PATH) . 'export.lib.inc.php';
+
+        $data = array(
+            array('UserName', 'ClassName')
+        );
+        foreach ($users as $user) {
+            $data[] = array($user['username'], $groupInfo['name']);
+        }
+        $filename = 'export_user_class_' . api_get_local_time();
+        Export::export_table_csv($data, $filename);
+        exit;
+    }
+}
+
 
 //Filter by Extra Fields
 $use_extra_fields = false;
@@ -182,9 +200,9 @@ if ($searchForm->validate()) {
     $filterData = $searchForm->getSubmitValues();
 }
 
-$data       = $usergroup->get($id);
-$list_in    = $usergroup->get_users_by_usergroup($id);
-$list_all   = $usergroup->get_users_by_usergroup();
+$data = $usergroup->get($id);
+$list_in = $usergroup->get_users_by_usergroup($id);
+$list_all = $usergroup->get_users_by_usergroup();
 
 $order = array('lastname');
 if (api_is_western_name_order()) {
@@ -280,6 +298,9 @@ echo Display::url(get_lang('AdvancedSearch'), '#', array('class' => 'advanced_op
 
 echo '<a href="usergroup_user_import.php">'.
     Display::return_icon('import_csv.png', get_lang('Import'), array(), ICON_SIZE_MEDIUM).'</a>';
+
+echo '<a href="'.api_get_self().'?id='.$id.'&action=export">'.
+    Display::return_icon('export_csv.png', get_lang('Export'), array(), ICON_SIZE_MEDIUM).'</a>';
 echo '</div>';
 
 echo '<div id="advanced_search_options" style="display:none">';

+ 2 - 2
main/admin/archive_cleanup.php

@@ -19,7 +19,7 @@ $this_section = SECTION_PLATFORM_ADMIN;
 api_protect_admin_script(true);
 
 // setting breadcrumbs
-$interbreadcrumb[]=array('url' => 'index.php','name' => get_lang('PlatformAdmin'));
+$interbreadcrumb[] = array('url' => 'index.php','name' => get_lang('PlatformAdmin'));
 
 $form = new FormValidator('archive_cleanup_form');
 $form->addElement('style_submit_button','proceed', get_lang('ArchiveDirCleanupProceedButton'),'class="save"');
@@ -63,4 +63,4 @@ if (!empty($message)) {
     echo $message;
 }
 $form->display();
-Display::display_footer();
+Display::display_footer();

+ 3 - 4
main/admin/configure_extensions.php

@@ -26,7 +26,7 @@ if (isset($_POST['activeExtension'])) {
 					WHERE variable="service_visio"
 					AND subkey="active"';
 			$rs = Database::query($sql);
-			if(Database::affected_rows()>0) {
+			if (Database::affected_rows($rs)>0) {
 				// select all the courses and insert the tool inside
 				$sql = 'SELECT id FROM '.Database::get_main_table(TABLE_MAIN_COURSE);
 				$rs = Database::query($sql);
@@ -105,7 +105,7 @@ if (isset($_POST['activeExtension'])) {
 
 			$rs = Database::query($sql);
 
-			if(Database::affected_rows()>0){
+			if (Database::affected_rows($rs)>0){
 				$message = get_lang('ServiceActivated');
 			}
 
@@ -441,8 +441,7 @@ Display::display_header($nameTool);
 	</div>
 	*/
     /*
-	
-    
+   
 	<!-- SEARCH -->
 	<div id="main_search">
 		<div id="extension_header_search" class="accordion_header">

+ 16 - 10
main/admin/configure_homepage.php

@@ -169,18 +169,24 @@ $homef = array($menuf, $newsf, $topf, $noticef, $menutabs, $mtloggedin);
 
 // If language-specific file does not exist, create it by copying default file
 foreach ($homef as $my_file) {
-	if (api_is_multiple_url_enabled()) {
-		if (!file_exists($homep_new.$my_file.'_'.$lang.$ext)) {
-			copy($homep.$my_file.$ext, $homep_new.$my_file.'_'.$lang.$ext);
-		}
-	} else {
-		if (!file_exists($homep.$my_file.'_'.$lang.$ext)) {
-			copy($homep.$my_file.$ext, $homep.$my_file.'_'.$lang.$ext);
-		}
-	}
+    if (api_is_multiple_url_enabled()) {
+        if (!file_exists($homep_new.$my_file.'_'.$lang.$ext)) {
+            if (!file_exists($homep.$my_file.$ext)) {
+                touch($homep.$my_file.$ext);
+            }
+            @copy($homep.$my_file.$ext, $homep_new.$my_file.'_'.$lang.$ext);
+        }
+    } else {
+        if (!file_exists($homep.$my_file.'_'.$lang.$ext)) {
+            if (!file_exists($homep.$my_file.$ext)) {
+                touch($homep.$my_file.$ext);
+            }
+            @copy($homep.$my_file.$ext, $homep.$my_file.'_'.$lang.$ext);
+        }
+    }
 }
 if (api_is_multiple_url_enabled()) {
-	$homep = $homep_new;
+    $homep = $homep_new;
 }
 
 // Check WCAG settings and prepare edition using WCAG

+ 11 - 10
main/admin/course_export.php

@@ -25,9 +25,10 @@ $archivePath = api_get_path(SYS_ARCHIVE_PATH);
 $archiveURL = api_get_path(WEB_CODE_PATH).'course_info/download.php?archive=';
 
 $course_list = CourseManager::get_courses_list();
+$formSent = null;
 
-if ($_POST['formSent']) {
-	$formSent	=$_POST['formSent'];
+if (isset($_POST['formSent']) && $_POST['formSent']) {
+	$formSent = $_POST['formSent'];
 	$select_type=intval($_POST['select_type']);
 	$file_type = 'csv';
 	$courses = $selected_courses = array();
@@ -92,20 +93,20 @@ if (!empty($msg)) {
 
 <form method="post" action="<?php echo api_get_self(); ?>" style="margin:0px;">
     <input type="hidden" name="formSent" value="1">
-    <legend><?php echo $tool_name; ?></legend>    
+    <legend><?php echo $tool_name; ?></legend>
 <?php if (!empty($course_list)) { ?>
-<div class="control-group">    
-    
+<div class="control-group">
+
     <div class="controls">
-        <label class="radio" for="all-courses">            
+        <label class="radio" for="all-courses">
         <input id="all-courses" class="checkbox" type="radio" value="1" name="select_type" <?php if(!$formSent || ($formSent && $select_type == 1)) echo 'checked="checked"'; ?> onclick="javascript: if(this.checked){document.getElementById('div-course-list').style.display='none';}"/>
             <?php echo get_lang('ExportAllCoursesList')?>
-        </label>        
-        
+        </label>
+
         <label class="radio" for="select-courses">
             <input id="select-courses" class="checkbox" type="radio" value="2" name="select_type" <?php if($formSent && $select_type == 2) echo 'checked="checked"'; ?> onclick="javascript: if(this.checked){document.getElementById('div-course-list').style.display='block';}"/>
             <?php echo get_lang('ExportSelectedCoursesFromCoursesList')?>
-        </label>        
+        </label>
     </div>
 </div>
 <div id="div-course-list" style="<?php echo (!$formSent || ($formSent && $select_type == 1))?'display:none':'display:block';?>">
@@ -133,4 +134,4 @@ if (!empty($msg)) {
 <?php } else { echo get_lang('ThereAreNotCreatedCourses'); }?>
 </form>
 <?php
-Display :: display_footer();
+Display :: display_footer();

+ 3 - 3
main/admin/course_import.php

@@ -155,7 +155,7 @@ require_once api_get_path(LIBRARY_PATH).'fileManage.lib.php';
 require_once api_get_path(LIBRARY_PATH).'import.lib.php';
 $defined_auth_sources[] = PLATFORM_AUTH_SOURCE;
 
-if (is_array($extAuthSource)) {
+if (isset($extAuthSource) && is_array($extAuthSource)) {
     $defined_auth_sources = array_merge($defined_auth_sources, array_keys($extAuthSource));
 }
 
@@ -166,7 +166,7 @@ $interbreadcrumb[] = array('url' => 'index.php', 'name' => get_lang('PlatformAdm
 set_time_limit(0);
 Display :: display_header($tool_name);
 
-if ($_POST['formSent']) {
+if (isset($_POST['formSent']) && $_POST['formSent']) {
     if (empty($_FILES['import_file']['tmp_name'])) {
         $error_message = get_lang('UplUploadFailed');
         Display :: display_error_message($error_message, false);
@@ -187,7 +187,7 @@ if ($_POST['formSent']) {
     }
 }
 
-if (count($errors) != 0) {
+if (isset($errors) && count($errors) != 0) {
     $error_message = '<ul>';
     foreach ($errors as $index => $error_course) {
         $error_message .= '<li>'.get_lang('Line').' '.$error_course['line'].': <strong>'.$error_course['error'].'</strong>: ';

+ 1 - 1
main/admin/course_intro_pdf_import.php

@@ -74,7 +74,7 @@ if (count($errors) != 0) {
 <input type="hidden" name="formSent" value="1"/>
 </form>
 <div style="clear: both;"></div>
-<p><?php echo get_lang('PDFsMustLookLike'); ?> :</p>
+<p><?php echo get_lang('PDFsMustLookLike'); ?></p>
 
 <blockquote>
 <pre>

+ 3 - 3
main/admin/course_list.php

@@ -222,7 +222,7 @@ if (isset ($_POST['action'])) {
             $course_codes = $_POST['course'];
             if (count($course_codes) > 0) {
                 foreach ($course_codes as $course_code) {
-                    CourseManager :: delete_course($course_code);
+                    CourseManager::delete_course($course_code);
                     $obj_cat = new Category();
                     $obj_cat->update_category_delete($course_code);
                 }
@@ -298,8 +298,8 @@ if (isset ($_GET['search']) && $_GET['search'] == 'advanced') {
                 break;
         }
     }
-    if (isset ($_GET['delete_course'])) {
-        CourseManager :: delete_course($_GET['delete_course']);
+    if (isset($_GET['delete_course'])) {
+        CourseManager::delete_course($_GET['delete_course']);
         $obj_cat = new Category();
         $obj_cat->update_category_delete($_GET['delete_course']);
 

+ 1 - 0
main/admin/course_request_accepted.php

@@ -100,6 +100,7 @@ function get_number_of_requests() {
 function get_request_data($from, $number_of_items, $column, $direction) {
     $keyword = Database::escape_string(trim($_GET['keyword']));
     $course_request_table = Database :: get_main_table(TABLE_MAIN_COURSE_REQUEST);
+
     $sql = "SELECT id AS col0,
                    code AS col1,
                    title AS col2,

+ 3 - 3
main/admin/course_request_rejected.php

@@ -48,9 +48,9 @@ $keyword = Database::escape_string(trim($_GET['keyword']));
 
 if ($course_validation_feature) {
 
-/**
- * Acceptance and creation of the requested course.
- */
+    /**
+     * Acceptance and creation of the requested course.
+     */
     if (!empty($accept_course_request)) {
         $course_request_code = CourseRequestManager::get_course_request_code($accept_course_request);
         $course_id = CourseRequestManager::accept_course_request($accept_course_request);

+ 0 - 1
main/admin/dashboard_add_courses_to_user.php

@@ -17,7 +17,6 @@ require_once '../inc/lib/xajax/xajax.inc.php';
 
 global $_configuration;
 
-// create an ajax object
 $xajax = new xajax();
 $xajax->registerFunction('search_courses');
 

+ 0 - 1
main/admin/dashboard_add_users_to_user.php

@@ -11,7 +11,6 @@ $language_file = 'admin';
 // resetting the course id
 $cidReset = true;
 
-// including some necessary dokeos files
 require_once '../inc/global.inc.php';
 require_once '../inc/lib/xajax/xajax.inc.php';
 

+ 22 - 12
main/admin/db.php

@@ -5,7 +5,7 @@
 */
 
 // Language files that should be included
-$language_file = array('admin', 'registration','userInfo');
+$language_file = array('admin', 'registration', 'userInfo');
 
 $cidReset = true;
 
@@ -36,10 +36,10 @@ function text_longitud(){
 
 // Database table definitions
 if (!empty($_GET['message'])) {
-	$message = urldecode($_GET['message']);
+    $message = urldecode($_GET['message']);
 }
 
-$interbreadcrumb[] = array ('url' => 'index.php', 'name' => get_lang('PlatformAdmin'));
+$interbreadcrumb[] = array('url' => 'index.php', 'name' => get_lang('PlatformAdmin'));
 $tool_name = get_lang('AddGroups');
 
 // Create the form
@@ -47,25 +47,35 @@ $form = new FormValidator('group_add');
 $form->addElement('header', $tool_name);
 
 // name
-$form->addElement('text', 'name', get_lang('Name'), array('size'=>60, 'maxlength'=>120));
+$form->addElement('text', 'name', get_lang('Name'), array('size' => 60, 'maxlength' => 120));
 $form->applyFilter('name', 'html_filter');
 $form->applyFilter('name', 'trim');
 $form->addRule('name', get_lang('ThisFieldIsRequired'), 'required');
 
 // Description
-$form->addElement('textarea', 'description', get_lang('Description'), array('rows'=>3, 'cols'=>58, 'onKeyDown' => "text_longitud()", 'onKeyUp' => "text_longitud()"));
+$form->addElement(
+    'textarea',
+    'description',
+    get_lang('Description'),
+    array('rows' => 3, 'cols' => 58, 'onKeyDown' => "text_longitud()", 'onKeyUp' => "text_longitud()")
+);
 $form->applyFilter('description', 'html_filter');
 $form->applyFilter('description', 'trim');
 
 // url
-$form->addElement('text', 'url', get_lang('URL'), array('size'=>35));
+$form->addElement('text', 'url', get_lang('URL'), array('size' => 35));
 $form->applyFilter('url', 'html_filter');
 $form->applyFilter('url', 'trim');
 
 // Picture
 $form->addElement('file', 'picture', get_lang('AddPicture'));
-$allowed_picture_types = array ('jpg', 'jpeg', 'png', 'gif');
-$form->addRule('picture', get_lang('OnlyImagesAllowed').' ('.implode(',', $allowed_picture_types).')', 'filetype', $allowed_picture_types);
+$allowed_picture_types = array('jpg', 'jpeg', 'png', 'gif');
+$form->addRule(
+    'picture',
+    get_lang('OnlyImagesAllowed').' ('.implode(',', $allowed_picture_types).')',
+    'filetype',
+    $allowed_picture_types
+);
 
 //Group Parentship
 $groups = array();
@@ -77,8 +87,8 @@ $form->addElement('select', 'parent_group', get_lang('GroupParentship'), $groups
 
 // Status
 $status = array();
-$status[GROUP_PERMISSION_OPEN] 		= get_lang('Open');
-$status[GROUP_PERMISSION_CLOSED]	= get_lang('Closed');
+$status[GROUP_PERMISSION_OPEN] = get_lang('Open');
+$status[GROUP_PERMISSION_CLOSED] = get_lang('Closed');
 
 $form->addElement('select', 'visibility', get_lang('GroupPermissions'), $status);
 
@@ -153,8 +163,8 @@ if( $form->validate()) {
 // Display form
 Display::display_header($tool_name);
 //api_display_tool_title($tool_name);
-if(!empty($message)){
-	Display::display_normal_message(stripslashes($message));
+if (!empty($message)) {
+    Display::display_normal_message(stripslashes($message));
 }
 $form->display();
 

+ 1 - 1
main/admin/group_list.php

@@ -397,4 +397,4 @@ if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
         $table->set_form_actions(array('delete' => get_lang('DeleteFromPlatform')));
     $table->display();
 }
-Display :: display_footer();
+Display :: display_footer();

+ 5 - 6
main/admin/inactive_user_list.php

@@ -2,9 +2,9 @@
 /* For licensing terms, see /license.txt */
 /**
     @author Andre Boivin base code
-	@author Julio Montoya fixing lot of little details  
+	@author Julio Montoya fixing lot of little details
 	@todo this script is not ready for a production use that's why I'm commenting the function delete_inactive_student
-	
+
 *	@package chamilo.admin
 *	script pour effacer les user inactif depuis x temps
 */
@@ -34,12 +34,11 @@ Display :: display_header($tool_name, "");
 //On sélectionne les user élèves
 $sql = "SELECT user_id FROM ".$table_user." user WHERE user.status= '5' ORDER by lastname " ;
 $result = Database::query($sql);
-	
-while($row = Database::fetch_array($result)) {   
+
+while($row = Database::fetch_array($result)) {
     $user_id = $row['user_id'];
     //  pour chaque élève, on trouve la dernière connexion
     //$last_connection_date = UserManager:: delete_inactive_student($user_id, 2, true);
 }
-    
-/*  		FOOTER    */
+
 Display :: display_footer();

+ 4 - 25
main/admin/languages.php

@@ -28,6 +28,7 @@ require_once 'sub_language.class.php';
 $this_section = SECTION_PLATFORM_ADMIN;
 
 api_protect_admin_script();
+$action = isset($_GET['action']) ? $_GET['action'] : null;
 
 //Ajax request
 if (isset($_POST['sent_http_request'])) {
@@ -50,7 +51,6 @@ if (isset($_POST['sent_http_request'])) {
     exit;
 }
 
-
 $htmlHeadXtra[] = '<script>
  $(document).ready(function() {
 
@@ -124,13 +124,6 @@ $htmlHeadXtra[] = '<script>
 $tbl_admin_languages = Database :: get_main_table(TABLE_MAIN_LANGUAGE);
 $tbl_settings_current = Database :: get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
 
-/*
-  ==============================================================================
-  STORING THE CHANGES
-  ==============================================================================
- */
-$action = isset($_GET['action']) ? $_GET['action'] : null;
-
 // we change the availability
 if ($action == 'makeunavailable') {
     if (isset($_GET['id']) && $_GET['id'] == strval(intval($_GET['id']))) {
@@ -186,11 +179,6 @@ if (isset($_POST['Submit']) && $_POST['Submit']) {
     }
 }
 
-/*
-  ==============================================================================
-  MAIN CODE
-  ==============================================================================
- */
 // setting the name of the tool
 $tool_name = get_lang('PlatformLanguages');
 
@@ -221,12 +209,6 @@ $sql_select_lang = "SELECT * FROM $tbl_settings_current WHERE  category='Languag
 $result_select_lang = Database::query($sql_select_lang);
 $row_lang = Database::fetch_array($result_select_lang);
 
-/*
-  --------------------------------------
-  DISPLAY THE TABLE
-  --------------------------------------
-*/
-
 // the table data
 $language_data = array();
 while ($row = Database::fetch_array($result_select)) {
@@ -278,6 +260,9 @@ while ($row = Database::fetch_array($result_select)) {
             $allow_add_term_sub_language = "&nbsp;<a href='sub_language.php?action=registersublanguage&id=" . Security::remove_XSS($all_information_of_sub_language['parent_id']) . "&sub_language_id=" . Security::remove_XSS($row['id']) . "'>" . Display::return_icon('2rightarrow.gif', get_lang('AddWordForTheSubLanguage'), array('width' => ICON_SIZE_SMALL, 'height' => ICON_SIZE_SMALL)) . "</a>";
             $allow_delete_sub_language = "&nbsp;<a href='sub_language_add.php?action=deletesublanguage&id=" . Security::remove_XSS($all_information_of_sub_language['parent_id']) . "&sub_language_id=" . Security::remove_XSS($row['id']) . "'>" . Display::return_icon('delete.png', get_lang('DeleteSubLanguage'), array('width' => ICON_SIZE_SMALL, 'height' => ICON_SIZE_SMALL)) . "</a>";
         }
+    } else {
+        $allow_use_sub_language = '';
+        $allow_add_term_sub_language = '';
     }
 
     if ($row['english_name'] == $row_lang['selected_value']) {
@@ -306,10 +291,4 @@ $table->set_form_actions($form_actions);
 echo '<div id="id_content_message">&nbsp;</div>';
 $table->display();
 
-/*
-  ==============================================================================
-  FOOTER
-  ==============================================================================
- */
 Display :: display_footer();
-?>

+ 33 - 33
main/admin/promotions.php

@@ -25,13 +25,13 @@ $interbreadcrumb[] = array('url' => 'career_dashboard.php','name' => get_lang('C
 $action = isset($_GET['action']) ? $_GET['action'] : null;
 
 $check = Security::check_token('request');
-$token = Security::get_token();    
+$token = Security::get_token();
 
 if ($action == 'add') {
     $interbreadcrumb[]=array('url' => 'promotions.php','name' => get_lang('Promotions'));
     $interbreadcrumb[]=array('url' => '#','name' => get_lang('Add'));
 } elseif ($action == 'edit') {
-    $interbreadcrumb[]=array('url' => 'promotions.php','name' => get_lang('Promotions'));    
+    $interbreadcrumb[]=array('url' => 'promotions.php','name' => get_lang('Promotions'));
     $interbreadcrumb[]=array('url' => '#','name' => get_lang('Edit'));
 } else {
     $interbreadcrumb[]=array('url' => '#','name' => get_lang('Promotions'));
@@ -43,7 +43,7 @@ Display::display_header($tool_name);
 // Tool name
 if (isset($_GET['action']) && $_GET['action'] == 'add') {
     $tool = 'Add';
-    $interbreadcrumb[] = array ('url' => api_get_self(), 'name' => get_lang('Promotion'));    
+    $interbreadcrumb[] = array ('url' => api_get_self(), 'name' => get_lang('Promotion'));
 }
 if (isset($_GET['action']) && $_GET['action'] == 'edit') {
     $tool = 'Modify';
@@ -54,31 +54,31 @@ $url            = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_promotions';
 //The order is important you need to check the model.ajax.php the $column variable
 $columns        = array(get_lang('Name'),get_lang('Career'),get_lang('Description'),get_lang('Actions'));
 $column_model   = array(
-                        array('name'=>'name',           'index'=>'name',        'width'=>'180',   'align'=>'left'),
-                        array('name'=>'career',         'index'=>'career',      'width'=>'100',  'align'=>'left'),
-                        array('name'=>'description',    'index'=>'description', 'width'=>'500',  'align'=>'left','sortable'=>'false'),
-                        array('name'=>'actions',        'index'=>'actions',     'width'=>'100',  'align'=>'left','formatter'=>'action_formatter','sortable'=>'false'),
-                       );                        
+    array('name'=>'name',           'index'=>'name',        'width'=>'180',   'align'=>'left'),
+    array('name'=>'career',         'index'=>'career',      'width'=>'100',  'align'=>'left'),
+    array('name'=>'description',    'index'=>'description', 'width'=>'500',  'align'=>'left','sortable'=>'false'),
+    array('name'=>'actions',        'index'=>'actions',     'width'=>'100',  'align'=>'left','formatter'=>'action_formatter','sortable'=>'false'),
+);
 $extra_params['autowidth'] = 'true'; //use the width of the parent
 //$extra_params['editurl'] = $url; //use the width of the parent
 
 $extra_params['height'] = 'auto'; //use the width of the parent
 //With this function we can add actions to the jgrid
 $action_links = 'function action_formatter (cellvalue, options, rowObject) {
-                    return \'<a href="add_sessions_to_promotion.php?id=\'+options.rowId+\'">'.Display::return_icon('session_to_promotion.png',get_lang('SubscribeSessionsToPromotions'),'',ICON_SIZE_SMALL).'</a>'.
-                    '&nbsp;<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=copy&id=\'+options.rowId+\'">'.Display::return_icon('copy.png',get_lang('Copy'),'',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="add_sessions_to_promotion.php?id=\'+options.rowId+\'">'.Display::return_icon('session_to_promotion.png',get_lang('SubscribeSessionsToPromotions'),'',ICON_SIZE_SMALL).'</a>'.
+    '&nbsp;<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=copy&id=\'+options.rowId+\'">'.Display::return_icon('copy.png',get_lang('Copy'),'',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() { 
-    <?php 
-         echo Display::grid_js('promotions',  $url,$columns,$column_model,$extra_params,array(), $action_links, true);       
+$(function() {
+    <?php
+         echo Display::grid_js('promotions',  $url,$columns,$column_model,$extra_params,array(), $action_links, true);
     ?>
 });
-</script>   
+</script>
 <?php
 $promotion = new Promotion();
 
@@ -99,22 +99,22 @@ switch ($action) {
         }
 
         $url  = api_get_self().'?action='.Security::remove_XSS($_GET['action']);
-        $form = $promotion->return_form($url, 'add');    
+        $form = $promotion->return_form($url, 'add');
 
         // The validation or display
-        if ($form->validate()) {            
+        if ($form->validate()) {
             if ($check) {
-                $values = $form->exportValues();       
-                $res    = $promotion->save($values);            
+                $values = $form->exportValues();
+                $res    = $promotion->save($values);
                 if ($res) {
                     Display::display_confirmation_message(get_lang('ItemAdded'));
                 }
-            }            
+            }
             $promotion->display();
         } else {
-            echo '<div class="actions">';        
+            echo '<div class="actions">';
             echo Display::url(Display::return_icon('back.png',get_lang('Back'),'',ICON_SIZE_MEDIUM), api_get_self());
-            echo '</div>';            
+            echo '</div>';
             $form->addElement('hidden', 'sec_token');
             $form->setConstants(array('sec_token' => $token));
             $form->display();
@@ -122,13 +122,13 @@ switch ($action) {
         break;
     case 'edit':
         //Editing 
-        $url  = api_get_self().'?action='.Security::remove_XSS($_GET['action']).'&id='.intval($_GET['id']);    
+        $url  = api_get_self().'?action='.Security::remove_XSS($_GET['action']).'&id='.intval($_GET['id']);
         $form = $promotion->return_form($url, 'edit');
 
         // The validation or display
-        if ($form->validate()) {            
+        if ($form->validate()) {
             if ($check) {
-                $values = $form->exportValues();                    
+                $values = $form->exportValues();
                 $res    = $promotion->update($values);
                 $promotion->update_all_sessions_status_by_promotion_id($values['id'], $values['status']);  
                 if ($res) {
@@ -137,15 +137,15 @@ switch ($action) {
             }            
             $promotion->display();
         } else {
-            echo '<div class="actions">';        
+            echo '<div class="actions">';
             echo Display::url(Display::return_icon('back.png',get_lang('Back'),'',ICON_SIZE_MEDIUM), api_get_self());
-            echo '</div>';            
+            echo '</div>';
             $form->addElement('hidden', 'sec_token');
             $form->setConstants(array('sec_token' => $token));
             $form->display();
         }
         break;
-    case 'delete':        
+    case 'delete':
         if ($check) {
             // Action handling: deleting an obj
             $res = $promotion->delete($_GET['id']);
@@ -153,17 +153,17 @@ switch ($action) {
                 Display::display_confirmation_message(get_lang('ItemDeleted'));
             }
         }
-        $promotion->display();        
+        $promotion->display();
         break;
     case 'copy':
         if (api_get_session_id() != 0 && !api_is_allowed_to_session_edit(false, true)) {
             api_not_allowed();
         }
         if ($check) {
-            $res = $promotion->copy($_GET['id'], null, true);    
+            $res = $promotion->copy($_GET['id'], null, true);
             if ($res) {
                 Display::display_confirmation_message(get_lang('ItemCopied').' - '.get_lang('ExerciseAndLPsAreInvisibleInTheNewCourse'));
-            }            
+            }
         }
         $promotion->display();
         break;

+ 181 - 47
main/admin/resume_session.php

@@ -24,26 +24,42 @@ $interbreadcrumb[] = array('url' => 'index.php','name' => get_lang('PlatformAdmi
 $interbreadcrumb[] = array('url' => 'session_list.php','name' => get_lang('SessionList'));
 
 // Database Table Definitions
-$tbl_session						= Database::get_main_table(TABLE_MAIN_SESSION);
-$tbl_session_rel_class				= Database::get_main_table(TABLE_MAIN_SESSION_CLASS);
-$tbl_session_rel_course				= Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
-$tbl_course							= Database::get_main_table(TABLE_MAIN_COURSE);
-$tbl_user							= Database::get_main_table(TABLE_MAIN_USER);
-$tbl_session_rel_user				= Database::get_main_table(TABLE_MAIN_SESSION_USER);
-$tbl_session_rel_course_rel_user	= Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
-$tbl_session_category				= Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
-
-$table_access_url_user              = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
-
-$sql = 'SELECT name, nbr_courses, nbr_users, nbr_classes, DATE_FORMAT(date_start,"%d-%m-%Y") as date_start, DATE_FORMAT(date_end,"%d-%m-%Y") as date_end, lastname, firstname, username, session_admin_id, nb_days_access_before_beginning, nb_days_access_after_end, session_category_id, visibility
-		FROM '.$tbl_session.' LEFT JOIN '.$tbl_user.' ON id_coach = user_id
+$tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
+$tbl_session_rel_class = Database::get_main_table(TABLE_MAIN_SESSION_CLASS);
+$tbl_session_rel_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
+$tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
+$tbl_user = Database::get_main_table(TABLE_MAIN_USER);
+$tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
+$tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
+$tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
+$table_access_url_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
+
+$sql = 'SELECT
+            name,
+            nbr_courses,
+            nbr_users,
+            nbr_classes,
+            DATE_FORMAT(date_start,"%d-%m-%Y") as date_start,
+            DATE_FORMAT(date_end,"%d-%m-%Y") as date_end,
+            lastname,
+            firstname,
+            username,
+            session_admin_id,
+            nb_days_access_before_beginning,
+            nb_days_access_after_end,
+            session_category_id,
+            visibility
+		FROM '.$tbl_session.'
+		LEFT JOIN '.$tbl_user.'
+		ON id_coach = user_id
 		WHERE '.$tbl_session.'.id='.$id_session;
 
 $rs      = Database::query($sql);
 $session = Database::store_result($rs);
 $session = $session[0];
 
-$sql = 'SELECT name FROM  '.$tbl_session_category.' WHERE id = "'.intval($session['session_category_id']).'"';
+$sql = 'SELECT name FROM  '.$tbl_session_category.'
+        WHERE id = "'.intval($session['session_category_id']).'"';
 $rs = Database::query($sql);
 $session_category = '';
 
@@ -57,18 +73,31 @@ $action = isset($_GET['action']) ? $_GET['action'] : null;
 
 $url_id = api_get_current_access_url_id();
 
-switch($action) {
+switch ($action) {
+    case 'move_up':
+        SessionManager::moveUp($id_session, $_GET['course_code']);
+        header('Location: resume_session.php?id_session='.$id_session);
+        exit;
+        break;
+    case 'move_down':
+        SessionManager::moveDown($id_session, $_GET['course_code']);
+        header('Location: resume_session.php?id_session='.$id_session);
+        exit;
+        break;
     case 'add_user_to_url':
         $user_id = $_REQUEST['user_id'];
         $result = UrlManager::add_user_to_url($user_id, $url_id);
         $user_info = api_get_user_info($user_id);
         if ($result) {
-            $message = Display::return_message(get_lang('UserAdded').' '.api_get_person_name($user_info['firstname'], $user_info['lastname']), 'confirm');
+            $message = Display::return_message(
+                get_lang('UserAdded').' '.api_get_person_name($user_info['firstname'], $user_info['lastname']),
+                'confirm'
+            );
         }
         break;
     case 'delete':
         $idChecked = $_GET['idChecked'];
-        if(is_array($idChecked)) {
+        if (is_array($idChecked)) {
             $my_temp = array();
             foreach ($idChecked as $id){
                 $my_temp[]= Database::escape_string($id);// forcing the escape_string
@@ -84,7 +113,7 @@ switch($action) {
             Database::query("UPDATE $tbl_session SET nbr_courses=nbr_courses-$nbr_affected_rows WHERE id='$id_session'");
         }
 
-        if(!empty($_GET['class'])){
+        if (!empty($_GET['class'])){
             Database::query("DELETE FROM $tbl_session_rel_class WHERE session_id='$id_session' AND class_id=".Database::escape_string($_GET['class']));
             $nbr_affected_rows=Database::affected_rows();
             Database::query("UPDATE $tbl_session SET nbr_classes=nbr_classes-$nbr_affected_rows WHERE id='$id_session'");
@@ -113,9 +142,14 @@ if (!empty($message)) {
     echo $message;
 }
 
-echo Display::page_header(Display::return_icon('session.png', get_lang('Session')).' '.$session['name']);
+echo Display::page_header(
+    Display::return_icon('session.png', get_lang('Session')).' '.$session['name']
+);
 
-$url = Display::url(Display::return_icon('edit.png', get_lang('Edit'), array(), ICON_SIZE_SMALL), "session_edit.php?page=resume_session.php&id=$id_session");
+$url = Display::url(
+    Display::return_icon('edit.png', get_lang('Edit'), array(), ICON_SIZE_SMALL),
+    "session_edit.php?page=resume_session.php&id=$id_session"
+);
 echo Display::page_subheader(get_lang('GeneralProperties').$url);
 
 ?>
@@ -139,7 +173,6 @@ echo Display::page_subheader(get_lang('GeneralProperties').$url);
 			echo get_lang('NoTimeLimits');
 		else {
             if ($session['date_start'] != '00-00-0000') {
-            	//$session['date_start'] = Display::tag('i', get_lang('NoTimeLimits'));
                 $session['date_start'] =  get_lang('From').' '.$session['date_start'];
             } else {
             	$session['date_start'] = '';
@@ -176,7 +209,14 @@ echo Display::page_subheader(get_lang('GeneralProperties').$url);
 		<?php echo api_ucfirst(get_lang('SessionVisibility')) ?> :
 	</td>
 	<td>
-		<?php if ($session['visibility']==1) echo get_lang('ReadOnly'); elseif($session['visibility']==2) echo get_lang('Visible');elseif($session['visibility']==3) echo api_ucfirst(get_lang('Invisible'))  ?>
+		<?php
+        if ($session['visibility']==1)
+            echo get_lang('ReadOnly');
+        elseif($session['visibility']==2)
+            echo get_lang('Visible');
+        elseif($session['visibility']==3)
+            echo api_ucfirst(get_lang('Invisible'));
+        ?>
 	</td>
 </tr>
 
@@ -190,18 +230,33 @@ if ($multiple_url_is_on) {
     echo '</td>';
     echo '<td>';
     $url_list = UrlManager::get_access_url_from_session($id_session);
-    foreach($url_list as $url_data) {
+    foreach ($url_list as $url_data) {
         echo $url_data['url'].'<br />';
     }
     echo '</td></tr>';
 }
+
+if (SessionManager::durationPerUserIsEnabled()) {
+    $sessionInfo = api_get_session_info($id_session);
+    echo '<tr><td>';
+    echo get_lang('Duration');
+    echo '</td>';
+    echo '<td>';
+    echo $sessionInfo['duration'].' ';
+    echo get_lang('Days');
+    echo '</td></tr>';
+
+}
 ?>
 </table>
 <br />
 
 <?php
 
-$url = Display::url(Display::return_icon('edit.png', get_lang('Edit'), array(), ICON_SIZE_SMALL), "add_courses_to_session.php?page=resume_session.php&id_session=$id_session");
+$url = Display::url(
+    Display::return_icon('edit.png', get_lang('Edit'), array(), ICON_SIZE_SMALL),
+    "add_courses_to_session.php?page=resume_session.php&id_session=$id_session"
+);
 echo Display::page_subheader(get_lang('CourseList').$url);
 
 ?>
@@ -211,37 +266,56 @@ echo Display::page_subheader(get_lang('CourseList').$url);
 <tr>
   <th width="35%"><?php echo get_lang('CourseTitle'); ?></th>
   <th width="30%"><?php echo get_lang('CourseCoach'); ?></th>
-  <th width="20%"><?php echo get_lang('UsersNumber'); ?></th>
-  <th width="15%"><?php echo get_lang('Actions'); ?></th>
+  <th width="10%"><?php echo get_lang('UsersNumber'); ?></th>
+  <th width="25%"><?php echo get_lang('Actions'); ?></th>
 </tr>
 <?php
-if ($session['nbr_courses'] == 0){
+if ($session['nbr_courses'] == 0) {
 	echo '<tr>
 			<td colspan="4">'.get_lang('NoCoursesForThisSession').'</td>
 		</tr>';
 } else {
 	// select the courses
+
+    $orderBy = "ORDER BY title";
+    if (SessionManager::orderCourseIsEnabled()) {
+        $orderBy = "ORDER BY position";
+    }
+
 	$sql = "SELECT code,title,visual_code, nbr_users
-			FROM $tbl_course,$tbl_session_rel_course
-			WHERE course_code = code
-			AND	id_session='$id_session'
-			ORDER BY title";
-	$result=Database::query($sql);
-	$courses=Database::store_result($result);
+			FROM $tbl_course, $tbl_session_rel_course
+			WHERE
+			    course_code = code AND
+			    id_session='$id_session'
+			$orderBy";
+
+    $result = Database::query($sql);
+    $courses = Database::store_result($result);
+    $count = 0;
 	foreach ($courses as $course) {
 		//select the number of users
 
-		$sql = " SELECT count(*) FROM $tbl_session_rel_user sru, $tbl_session_rel_course_rel_user srcru
-				WHERE srcru.id_user = sru.id_user AND srcru.id_session = sru.id_session AND srcru.course_code = '".Database::escape_string($course['code'])."'
-				AND sru.relation_type<>".SESSION_RELATION_TYPE_RRHH." AND srcru.id_session = '".intval($id_session)."'";
+		$sql = "SELECT count(*)
+                FROM $tbl_session_rel_user sru, $tbl_session_rel_course_rel_user srcru
+				WHERE
+				    srcru.id_user = sru.id_user AND
+				    srcru.id_session = sru.id_session AND
+				    srcru.course_code = '".Database::escape_string($course['code'])."' AND
+				    sru.relation_type <> ".SESSION_RELATION_TYPE_RRHH." AND
+				    srcru.id_session = '".intval($id_session)."'";
 
 		$rs = Database::query($sql);
-		$course['nbr_users'] = Database::result($rs,0,0);
+		$course['nbr_users'] = Database::result($rs, 0, 0);
 
 		// Get coachs of the courses in session
 
-		$sql = "SELECT user.lastname,user.firstname,user.username FROM $tbl_session_rel_course_rel_user session_rcru, $tbl_user user
-				WHERE session_rcru.id_user = user.user_id AND session_rcru.id_session = '".intval($id_session)."' AND session_rcru.course_code ='".Database::escape_string($course['code'])."' AND session_rcru.status=2";
+		$sql = "SELECT user.lastname,user.firstname,user.username
+                FROM $tbl_session_rel_course_rel_user session_rcru, $tbl_user user
+				WHERE
+				    session_rcru.id_user = user.user_id AND
+				    session_rcru.id_session = '".intval($id_session)."' AND
+				    session_rcru.course_code ='".Database::escape_string($course['code'])."' AND
+				    session_rcru.status=2";
 		$rs = Database::query($sql);
 
 		$coachs = array();
@@ -259,6 +333,36 @@ if ($session['nbr_courses'] == 0){
 			$coach = get_lang('None');
 		}
 
+        $orderButtons = null;
+
+        if (SessionManager::orderCourseIsEnabled()) {
+            $upIcon = 'up.png';
+            $urlUp = api_get_self().'?id_session='.$id_session.'&course_code='.$course['code'].'&action=move_up';
+
+            if ($count == 0) {
+                $upIcon = 'up_na.png';
+                $urlUp = '#';
+            }
+
+            $orderButtons = Display::url(
+                Display::return_icon($upIcon, get_lang('MoveUp')),
+                $urlUp
+            );
+
+            $downIcon = 'down.png';
+            $downUrl = api_get_self().'?id_session='.$id_session.'&course_code='.$course['code'].'&action=move_down';
+
+            if ($count +1 == count($courses)) {
+                $downIcon = 'down_na.png';
+                $downUrl = '#';
+            }
+
+            $orderButtons .= Display::url(
+                Display::return_icon($downIcon, get_lang('MoveDown')),
+                $downUrl
+            );
+        }
+
 		$orig_param = '&origin=resume_session';
 		//hide_course_breadcrumb the parameter has been added to hide the name of the course, that appeared in the default $interbreadcrumb
 		echo '
@@ -268,13 +372,16 @@ if ($session['nbr_courses'] == 0){
 			<td>'.$course['nbr_users'].'</td>
 			<td>
                 <a href="'.api_get_path(WEB_COURSE_PATH).$course['code'].'/?id_session='.$id_session.'">'.Display::return_icon('course_home.gif', get_lang('Course')).'</a>
+                '.$orderButtons.'
                 <a href="session_course_user_list.php?id_session='.$id_session.'&course_code='.$course['code'].'">'.Display::return_icon('user.png', get_lang('Edit'), '', ICON_SIZE_SMALL).'</a>
                 <a href="'.api_get_path(WEB_CODE_PATH).'/user/user_import.php?action=import&cidReq='.$course['code'].'&id_session='.$id_session.'">'.Display::return_icon('import_csv.png', get_lang('ImportUsersToACourse'), null, ICON_SIZE_SMALL).'</a>
 				<a href="../tracking/courseLog.php?id_session='.$id_session.'&cidReq='.$course['code'].$orig_param.'&hide_course_breadcrumb=1">'.Display::return_icon('statistics.gif', get_lang('Tracking')).'</a>&nbsp;
 				<a href="session_course_edit.php?id_session='.$id_session.'&page=resume_session.php&course_code='.$course['code'].''.$orig_param.'">'.Display::return_icon('edit.png', get_lang('Edit'), '', ICON_SIZE_SMALL).'</a>
 				<a href="'.api_get_self().'?id_session='.$id_session.'&action=delete&idChecked[]='.$course['code'].'" onclick="javascript:if(!confirm(\''.get_lang('ConfirmYourChoice').'\')) return false;">'.Display::return_icon('delete.png', get_lang('Delete')).'</a>
+
 			</td>
 		</tr>';
+        $count++;
 	}
 }
 ?>
@@ -283,14 +390,17 @@ if ($session['nbr_courses'] == 0){
 
 <?php
 
-$url = Display::url(Display::return_icon('edit.png', get_lang('Edit'), array(), ICON_SIZE_SMALL), "add_users_to_session.php?page=resume_session.php&id_session=$id_session");
-$url .= Display::url(Display::return_icon('import_csv.png', get_lang('ImportUsers'), array(), ICON_SIZE_SMALL), "session_user_import.php?id_session=$id_session");
+$url = Display::url(
+    Display::return_icon('edit.png', get_lang('Edit'), array(), ICON_SIZE_SMALL),
+    "add_users_to_session.php?page=resume_session.php&id_session=$id_session"
+);
+$url .= Display::url(
+    Display::return_icon('import_csv.png', get_lang('ImportUsers'), array(), ICON_SIZE_SMALL),
+    "session_user_import.php?id_session=$id_session"
+);
 echo Display::page_subheader(get_lang('UserList').$url);
-
 ?>
-
 <!--List of users -->
-
 <table class="data_table">
     <tr>
         <th>
@@ -343,15 +453,28 @@ if ($session['nbr_users']==0) {
                 $link_to_add_user_in_url = '<a href="resume_session.php?action=add_user_to_url&id_session='.$id_session.'&user_id='.$user['user_id'].'">'.$add.'</a>';
             }
         }
+
+        $editUrl = null;
+        if (SessionManager::durationPerUserIsEnabled()) {
+            if (isset($sessionInfo['duration']) && !empty($sessionInfo['duration'])) {
+                $editUrl = api_get_path(WEB_CODE_PATH) . 'admin/session_user_edit.php?session_id=' . $id_session . '&user_id=' . $user['user_id'];
+                $editUrl = Display::url(
+                    Display::return_icon('agenda.png', get_lang('SessionDurationEdit')),
+                    $editUrl
+                );
+            }
+        }
 		echo '<tr>
-                <td width="90%">
+                <td width="80%">
                     '.$user_link.'
                 </td>
                 <td>
+                    '.$editUrl.'
                     <a href="../mySpace/myStudents.php?student='.$user['user_id'].''.$orig_param.'">'.Display::return_icon('statistics.gif', get_lang('Reporting')).'</a>&nbsp;
                     <a href="session_course_user.php?id_user='.$user['user_id'].'&id_session='.$id_session.'">'.Display::return_icon('course.gif', get_lang('BlockCoursesForThisUser')).'</a>&nbsp;
                     <a href="'.api_get_self().'?id_session='.$id_session.'&action=delete&user='.$user['user_id'].'" onclick="javascript:if(!confirm(\''.get_lang('ConfirmYourChoice').'\')) return false;">'.Display::return_icon('delete.png', get_lang('Delete')).'</a>
                     '.$link_to_add_user_in_url.'
+
                 </td>
                 </tr>';
 	}
@@ -359,5 +482,16 @@ if ($session['nbr_users']==0) {
 ?>
 </table>
 <?php
-// footer
-Display :: display_footer();
+
+Display :: display_footer();
+
+/*
+ ALTER TABLE session_rel_course ADD COLUMN position int;
+ ALTER TABLE session_rel_course ADD COLUMN category varchar(255);
+
+ https://task.beeznest.com/issues/8317:
+
+ ALTER TABLE session ADD COLUMN duration int;
+ ALTER TABLE session_rel_user ADD COLUMN duration int;
+ *
+*/

+ 62 - 18
main/admin/session_add.php

@@ -97,29 +97,46 @@ function fill_coach_field (username) {
 
 if ($_POST['formSent']) {
 	$formSent = 1;
-	$name                  = $_POST['name'];
-	$year_start            = $_POST['year_start'];
-	$month_start           = $_POST['month_start'];
-	$day_start             = $_POST['day_start'];
-	$year_end              = $_POST['year_end'];
-	$month_end             = $_POST['month_end'];
-	$day_end               = $_POST['day_end'];
-	$nb_days_acess_before  = $_POST['nb_days_acess_before'];
-	$nb_days_acess_after   = $_POST['nb_days_acess_after'];
-	//$nolimit               = $_POST['nolimit'];
-	$coach_username        = $_POST['coach_username'];
-	$id_session_category   = $_POST['session_category'];
-	$id_visibility         = $_POST['session_visibility'];
-
-    $end_limit             = $_POST['end_limit'];
-    $start_limit           = $_POST['start_limit'];
+    $name = $_POST['name'];
+    $year_start = $_POST['year_start'];
+    $month_start = $_POST['month_start'];
+    $day_start = $_POST['day_start'];
+    $year_end = $_POST['year_end'];
+    $month_end = $_POST['month_end'];
+    $day_end = $_POST['day_end'];
+    $nb_days_acess_before = $_POST['nb_days_acess_before'];
+    $nb_days_acess_after = $_POST['nb_days_acess_after'];
+    $coach_username = $_POST['coach_username'];
+    $id_session_category = $_POST['session_category'];
+    $id_visibility = $_POST['session_visibility'];
+    $end_limit = $_POST['end_limit'];
+    $start_limit = $_POST['start_limit'];
+    $duration = isset($_POST['duration']) ? $_POST['duration'] : null;
 
     if (empty($end_limit) && empty($start_limit)) {
         $nolimit = 1;
     } else {
     	$nolimit = null;
     }
-	$return = SessionManager::create_session($name,$year_start,$month_start,$day_start,$year_end,$month_end,$day_end,$nb_days_acess_before,$nb_days_acess_after,$nolimit,$coach_username, $id_session_category,$id_visibility, $start_limit, $end_limit);
+
+    $return = SessionManager::create_session(
+        $name,
+        $year_start,
+        $month_start,
+        $day_start,
+        $year_end,
+        $month_end,
+        $day_end,
+        $nb_days_acess_before,
+        $nb_days_acess_after,
+        $nolimit,
+        $coach_username,
+        $id_session_category,
+        $id_visibility,
+        $start_limit,
+        $end_limit,
+        $duration
+    );
 
 	if ($return == strval(intval($return))) {
 		// integer => no error on session creation
@@ -394,6 +411,23 @@ for ($i=$thisYear-5;$i <= ($thisYear+5);$i++) {
         </div>
     </div>
 
+    <?php
+    if (SessionManager::durationPerUserIsEnabled()) {
+        ?>
+        <div class="control-group">
+            <label class="control-label">
+                <?php echo get_lang('SessionDurationTitle') ?> <br />
+            </label>
+            <div class="controls">
+                <input id="duration" type="text" name="duration" class="span1" maxlength="50" value="">
+                <br />
+                <?php echo get_lang('SessionDurationDescription') ?>
+            </div>
+        </div>
+
+    <?php
+    }
+    ?>
 
  <div class="control-group">
     <div class="controls">
@@ -433,6 +467,8 @@ function disable_endtime(select) {
         end_div.style.display = 'block';
      else
         end_div.style.display = 'none';
+
+    emptyDuration();
 }
 
 function disable_starttime(select) {
@@ -441,7 +477,15 @@ function disable_starttime(select) {
         start_div.style.display = 'block';
      else
         start_div.style.display = 'none';
+
+    emptyDuration();
+}
+
+function emptyDuration() {
+    if ($('#duration').val()) {
+        $('#duration').val('');
+    }
 }
 </script>
 <?php
-Display::display_footer();
+Display::display_footer();

+ 2 - 2
main/admin/session_category_add.php

@@ -31,7 +31,7 @@ $interbreadcrumb[]=array('url' => "session_category_list.php","name" => get_lang
 $tbl_user		= Database::get_main_table(TABLE_MAIN_USER);
 $tbl_session	= Database::get_main_table(TABLE_MAIN_SESSION);
 
-if ($_POST['formSent']) {
+if (isset($_POST['formSent']) && $_POST['formSent']) {
 	$formSent=1;
 	$name= $_POST['name'];
 	$year_start= $_POST['year_start'];
@@ -233,4 +233,4 @@ function setDisable(select) {
 </script>
 <?php
 Display::display_footer();
-?>
+

+ 1 - 4
main/admin/session_category_edit.php

@@ -220,11 +220,9 @@ for($i=$thisYear-5;$i <= ($thisYear+5);$i++)
 
   </td>
 </tr>
-
 </table>
-
 </form>
-<script type="text/javascript">
+<script>
 <?php if($year_start=="0000") echo "setDisable(document.form.nolimit);\r\n"; ?>
 function setDisable(select){
 	document.form.day_start.disabled = (select.checked) ? true : false;
@@ -237,4 +235,3 @@ function setDisable(select){
 </script>
 <?php
 Display::display_footer();
-?>

+ 8 - 17
main/admin/session_category_list.php

@@ -17,16 +17,14 @@ api_protect_admin_script(true);
 
 // setting the section (for the tabs)
 $this_section = SECTION_PLATFORM_ADMIN;
-$htmlHeadXtra[] =
-'<script>
-    function selectAll(idCheck,numRows,action) {
-        for(i=0;i<numRows;i++) {
-            idcheck = document.getElementById(idCheck+"_"+i);
-            if (action == "true"){
-                idcheck.checked = true;
-            } else {
-                idcheck.checked = false;
-            }
+$htmlHeadXtra[] = '<script>
+function selectAll(idCheck,numRows,action) {
+    for(i=0;i<numRows;i++) {
+        idcheck = document.getElementById(idCheck+"_"+i);
+        if (action == "true"){
+            idcheck.checked = true;
+        } else {
+            idcheck.checked = false;
         }
     }
 </script>';
@@ -75,9 +73,6 @@ if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
     } else {
         $where .= (empty($_REQUEST['keyword']) ? "" : " WHERE name LIKE '%".Database::escape_string(trim($_REQUEST['keyword']))."%'");
     }
-
-
-
     if (empty($where)) {
         $where = " WHERE access_url_id = ".api_get_current_access_url_id()." ";
     } else {
@@ -206,9 +201,7 @@ if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
             ?>
         </table>
         <br />
-
         <div align="left">
-
             <?php
             if ($num > $limit) {
                 if ($page) {
@@ -221,9 +214,7 @@ if (isset($_GET['search']) && $_GET['search'] == 'advanced') {
                 echo get_lang('Previous');
             }
             ?>
-
                 |
-
                 <?php
                 if ($nbr_results > $limit) {
                     ?>

+ 38 - 4
main/admin/session_edit.php

@@ -60,7 +60,7 @@ if (isset($_POST['formSent']) && $_POST['formSent']) {
 	$id_coach              = $_POST['id_coach'];
 	$id_session_category   = $_POST['session_category'];
 	$id_visibility         = $_POST['session_visibility'];
-
+    $duration = isset($_POST['duration']) ? $_POST['duration'] : null;
     $description = isset($_POST['description']) ? $_POST['description'] : null;
     $showDescription = isset($_POST['show_description']) ? 1 : 0;
 
@@ -91,7 +91,8 @@ if (isset($_POST['formSent']) && $_POST['formSent']) {
         $start_limit,
         $end_limit,
         $description,
-        $showDescription
+        $showDescription,
+        $duration
     );
 
 	if ($return == strval(intval($return))) {
@@ -376,6 +377,29 @@ if (!empty($return)) {
 
     <?php } ?>
 
+    <?php
+        if (SessionManager::durationPerUserIsEnabled()) {
+            if (empty($infos['duration'])) {
+                $duration = null;
+            } else {
+                $duration = $infos['duration'];
+            }
+            ?>
+            <div class="control-group">
+                <label class="control-label">
+                    <?php echo get_lang('SessionDurationTitle') ?> <br />
+                </label>
+                <div class="controls">
+                    <input id="duration" type="text" name="duration" class="span1" maxlength="50" value="<?php if($formSent) echo Security::remove_XSS($duration); else echo $duration; ?>">
+                    <br />
+                    <?php echo get_lang('SessionDurationDescription') ?>
+                </div>
+            </div>
+
+        <?php
+        }
+    ?>
+
     <div class="control-group">
         <div class="controls">
             <button class="save" type="submit" value="<?php echo get_lang('ModifyThisSession') ?>"><?php echo get_lang('ModifyThisSession') ?></button>
@@ -386,9 +410,11 @@ if (!empty($return)) {
 
 <script type="text/javascript">
 
-<?php if($year_start=="0000") echo "setDisable(document.form.nolimit);\r\n"; ?>
+<?php
+//if($year_start=="0000") echo "setDisable(document.form.nolimit);\r\n";
+?>
 
-function setDisable(select){
+function setDisable(select) {
 
 	document.form.day_start.disabled = (select.checked) ? true : false;
 	document.form.month_start.disabled = (select.checked) ? true : false;
@@ -419,6 +445,7 @@ function disable_endtime(select) {
         end_div.style.display = 'block';
      else
         end_div.style.display = 'none';
+    emptyDuration();
 }
 
 function disable_starttime(select) {
@@ -427,6 +454,13 @@ function disable_starttime(select) {
         start_div.style.display = 'block';
      else
         start_div.style.display = 'none';
+    emptyDuration();
+}
+
+function emptyDuration() {
+    if ($('#duration').val()) {
+        $('#duration').val('');
+    }
 }
 
 </script>

+ 0 - 1
main/admin/session_list.php

@@ -62,7 +62,6 @@ $actions = '
 $(function() {
     $("#course_name").on("change", function() {
        var courseId = $(this).val();
-       console.log(courseId);
        window.location = "'.$url.'?course_id="+courseId;
     });
 });

+ 97 - 0
main/admin/session_user_edit.php

@@ -0,0 +1,97 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+$language_file = 'admin';
+$cidReset = true;
+
+// including the global Chamilo file
+require_once '../inc/global.inc.php';
+
+api_protect_admin_script(true);
+
+$sessionId = isset($_GET['session_id']) ? $_GET['session_id'] : null;
+$userId = isset($_GET['user_id']) ? $_GET['user_id'] : null;
+
+SessionManager::protect_session_edit($sessionId);
+
+$sessionInfo = api_get_session_info($sessionId);
+if (empty($sessionInfo)) {
+    api_not_allowed(true);
+}
+
+if (!isset($sessionInfo['duration']) ||
+    isset($sessionInfo['duration']) && empty($sessionInfo['duration'])) {
+    api_not_allowed(true);
+}
+
+if (!SessionManager::durationPerUserIsEnabled()) {
+    api_not_allowed(true);
+}
+if (empty($sessionId) || empty($userId)) {
+    api_not_allowed(true);
+}
+
+$interbreadcrumb[] = array('url' => 'index.php', 'name' => get_lang('PlatformAdmin'));
+$interbreadcrumb[] = array('url' => 'session_list.php','name' => get_lang('SessionList'));
+$interbreadcrumb[] = array('url' => "resume_session.php?id_session=".$sessionId, "name" => get_lang('SessionOverview'));
+
+$form = new FormValidator('edit', 'post', api_get_self().'?session_id='.$sessionId.'&user_id='.$userId);
+$form->add_header(get_lang('EditUserSessionDuration'));
+$data = SessionManager::getUserSession($userId, $sessionId);
+$userInfo = api_get_user_info($userId);
+
+// Show current end date for the session for this user, if any
+$userAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
+    $sessionId,
+    $userId
+);
+if (count($userAccess) == 0) {
+    // User never accessed the session. End date is still open
+    $msg = sprintf(get_lang('UserNeverAccessedSessionDefaultDurationIsX'), $sessionInfo['duration']);
+} else {
+    // The user already accessed the session. Show a clear detail of the days count.
+    $duration = $sessionInfo['duration'];
+    if (!empty($data['duration'])) {
+        $duration = $duration + $data['duration'];
+    }
+    $days = SessionManager::getDayLeftInSession($sessionId, $userId, $duration);
+    $firstAccess = api_strtotime($userAccess['login_course_date'], 'UTC');
+    $firstAccessString = api_convert_and_format_date($userAccess['login_course_date'], DATE_FORMAT_SHORT, 'UTC');
+    if ($days > 0) {
+        $msg = sprintf(get_lang('FirstAccessWasXSessionDurationYEndDateInZDays'), $firstAccessString, $duration, $days);
+    } else {
+        $endDateInSeconds = $firstAccess + $duration*24*60*60;
+        $last = api_convert_and_format_date($endDateInSeconds, DATE_FORMAT_SHORT);
+        $msg = sprintf(get_lang('FirstAccessWasXSessionDurationYEndDateWasZ'), $firstAccessString, $duration, $last);
+    }
+}
+$form->addElement('html', sprintf(get_lang('UserXSessionY'), $userInfo['complete_name'], $sessionInfo['name']));
+$form->addElement('html', '<br>');
+$form->addElement('html', $msg);
+
+$form->addElement('text', 'duration', array(get_lang('ExtraDurationForUser'), null, get_lang('Days')));
+$form->addElement('button', 'submit', get_lang('Send'));
+
+if (empty($data['duration'])) {
+    $data['duration'] = 0;
+}
+$form->setDefaults($data);
+$message = null;
+if ($form->validate()) {
+    $duration = $form->getSubmitValue('duration');
+    // Only update if the duration is different from the default duration
+    if ($duration != 0) {
+        SessionManager::editUserSessionDuration($duration, $userId, $sessionId);
+        $message = Display::return_message(get_lang('ItemUpdated'), 'confirmation');
+    } else {
+        $message = Display::return_message(get_lang('DurationIsSameAsDefault'), 'warning');
+    }
+}
+
+// display the header
+Display::display_header(get_lang('Edit'));
+
+echo $message;
+$form->display();
+
+Display :: display_footer();

+ 2 - 2
main/admin/session_user_import.php

@@ -28,7 +28,7 @@ $tool_name = get_lang('ImportUsers');
 $session_id = isset($_GET['id_session']) ? intval($_GET['id_session']) : null;
 
 if (empty($session_id)) {
-    api_not_allowed(true);
+    api_not_allowed(true);    
 }
 
 $interbreadcrumb[] = array('url' => 'index.php', 'name' => get_lang('PlatformAdmin'));
@@ -43,7 +43,7 @@ if (isset($_POST['formSent']) && $_POST['formSent']) {
         $send_mail = $_POST['sendMail'] ? 1 : 0;
 
         // CSV
-        $users     = Import::csv_to_array($_FILES['import_file']['tmp_name']);
+        $users = Import::csv_to_array($_FILES['import_file']['tmp_name']);
         $user_list = array();
         foreach ($users as $user_data) {
             $username = $user_data['username'];

+ 9 - 8
main/admin/settings.lib.php

@@ -111,7 +111,7 @@ function handle_extensions()
 function handle_plugins()
 {
     $plugin_obj = new AppPlugin();
-
+    $token = Security::get_token();
      if (isset($_POST['submit_plugins'])) {
         store_plugins();
         // Add event to the system log.
@@ -126,7 +126,7 @@ function handle_plugins()
 
     //Plugins NOT installed
     echo Display::page_subheader(get_lang('Plugins'));
-    echo '<form class="form-horizontal" name="plugins" method="post" action="'.api_get_self().'?category='.Security::remove_XSS($_GET['category']).'">';
+    echo '<form class="form-horizontal" name="plugins" method="post" action="'.api_get_self().'?category='.Security::remove_XSS($_GET['category']).'&sec_token=' . $token . '">';
     echo '<table class="data_table">';
     echo '<tr>';
     echo '<th width="20px">';
@@ -238,6 +238,8 @@ function handle_stylesheets()
         }
     }
 
+    // Stylesheet upload.
+
     if (isset($_POST['stylesheet_upload'])) {
         if ($form->validate()) {
             $values = $form->exportValues();
@@ -717,6 +719,7 @@ function handle_search()
  * Wrapper for the templates
  *
  * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University, Belgium
+ * @author Julio Montoya.
  * @version August 2008
  * @since Dokeos 1.8.6
  */
@@ -747,7 +750,6 @@ function handle_templates() {
             $category = $_GET['category'];
             event_system(LOG_CONFIGURATION_SETTINGS_CHANGE, LOG_CONFIGURATION_SETTINGS_CATEGORY, $category, api_get_utc_datetime(), $user_id);
         }
-
         display_templates();
     }
 }
@@ -969,7 +971,7 @@ function add_edit_template() {
            if ($_GET['action'] == 'add') {
                $content_template = '<head>{CSS}<style type="text/css">.text{font-weight: normal;}</style></head><body>'.Database::escape_string($values['template_text']).'</body>';
                $sql = "INSERT INTO $table_system_template (title, content, image) VALUES ('".Database::escape_string($values['title'])."','".$content_template."','".Database::escape_string($new_file_name)."')";
-               $result = Database::query($sql);
+               Database::query($sql);
 
                // Display a feedback message.
                Display::display_confirmation_message(get_lang('TemplateAdded'));
@@ -981,7 +983,7 @@ function add_edit_template() {
                    $sql .= ", image = '".Database::escape_string($new_file_name)."'";
                }
                $sql .= " WHERE id='".Database::escape_string($_GET['id'])."'";
-               $result = Database::query($sql);
+               Database::query($sql);
 
                // Display a feedback message.
                Display::display_confirmation_message(get_lang('TemplateEdited'));
@@ -1030,7 +1032,7 @@ function delete_template($id) {
 
 /**
  * Returns the list of timezone identifiers used to populate the select
- *
+ * This function is called through a call_user_func() in the generate_settings_form function.
  * @return array List of timezone identifiers
  *
  * @author Guillaume Viguier <guillaume.viguier@beeznest.com>
@@ -1042,7 +1044,7 @@ function select_timezone_value() {
 
 /**
  * Returns an array containing the list of options used to populate the gradebook_number_decimals variable
- *
+ * This function is called through a call_user_func() in the generate_settings_form function.
  * @return array List of gradebook_number_decimals options
  *
  * @author Guillaume Viguier <guillaume.viguier@beeznest.com>
@@ -1106,7 +1108,6 @@ function generate_settings_form($settings, $settings_by_access_list) {
     }
 
     $default_values = array();
-
     $url_info = api_get_access_url($url_id);
 
     $i = 0;

+ 5 - 3
main/admin/settings.php

@@ -159,12 +159,12 @@ if (!empty($_GET['category']) && !in_array($_GET['category'], array('Plugins', '
                             $changeable = 1;
                         }
 
-                        $params = array('variable = ?' =>  array($key));
+                        $params = array('variable = ?' => array($key));
                         $data = api_get_settings_params($params);
 
                         if (!empty($data)) {
                             foreach ($data as $item) {
-                                $params = array('id' =>$item['id'], 'access_url_changeable' => $changeable);
+                                $params = array('id' => $item['id'], 'access_url_changeable' => $changeable);
                                 api_set_setting_simple($params);
                             }
                         }
@@ -443,7 +443,9 @@ if (!empty($_GET['category'])) {
         case 'Plugins':
             // Displaying the extensions: Plugins.
             // This will be available to all the sites (access_urls).
-            if (isset($_POST['submit_dashboard_plugins'])) {
+            $securityToken = Security::remove_XSS($_GET['sec_token']);
+            if (isset($_POST['submit_dashboard_plugins']) && Security::check_token($securityToken)) {
+                Security::clear_token();
                 $affected_rows = DashboardManager::store_dashboard_plugins($_POST);
                 if ($affected_rows) {
                     // add event to system log

+ 1 - 2
main/admin/statistics/index.php

@@ -81,7 +81,6 @@ switch ($_REQUEST['report']) {
     case 'courselastvisit':
         Statistics::print_course_last_visit();
         break;
-    //---
     case 'users':
         // total amount of users
         Statistics::print_stats(
@@ -135,4 +134,4 @@ switch ($_REQUEST['report']) {
         break;
 }
 
-Display::display_footer();
+Display::display_footer();

+ 50 - 3
main/admin/sub_language.class.php

@@ -4,7 +4,7 @@
 
 /**
  * SubLanguageManager class definition file
- * @package chamilo.admin.sublanguage 
+ * @package chamilo.admin.sublanguage
  * @todo clean this lib and move to main/inc/lib
  */
 class SubLanguageManager {
@@ -312,7 +312,7 @@ class SubLanguageManager {
     /**
      * Set platform language
      * @param Integer The language id
-     * @return void()
+     * @return bool
      */
     public static function set_platform_language($language_id) {
         if (empty($language_id) or (intval($language_id) != $language_id)) {
@@ -350,7 +350,6 @@ class SubLanguageManager {
      * @param    string  Children language path
      * @return   string  Parent language path or null
      */
-
     public static function get_parent_language_path($language_path) {
         $tbl_admin_languages = Database :: get_main_table(TABLE_MAIN_LANGUAGE);
         $tbl_settings_current = Database :: get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
@@ -363,4 +362,52 @@ class SubLanguageManager {
         return $row['dokeos_folder'];
     }
 
+    /**
+     * Get language matching isocode
+     * @param   string  $isocode The language isocode (en, es, fr, zh-TW, etc)
+     * @return  mixed  English name of the matching language, or false if no active language could be found
+     */
+    public static function getLanguageFromIsocode($isocode) {
+        $isocode = Database::escape_string($isocode);
+        $adminLanguagesTable = Database :: get_main_table(TABLE_MAIN_LANGUAGE);
+        // select language - if case several languages match, get the last (more recent) one
+        $sql = "SELECT english_name
+                FROM " . $adminLanguagesTable . "
+                WHERE isocode ='$isocode'
+                    AND available = 1
+                ORDER BY id
+                DESC LIMIT 1";
+        $res = Database::query($sql);
+        if (Database::num_rows($res) < 1) {
+            return false;
+        }
+        $row = Database::fetch_assoc($res);
+        return $row['english_name'];
+    }
+    /**
+     * Get best language in browser preferences
+     * @param   string  $preferences The browser-configured language preferences (e.g. "en,es;q=0.7;en-us;q=0.3", etc)
+     * @return  mixed  English name of the matching language, or false if no active language could be found
+     */
+    public static function getLanguageFromBrowserPreference($preferences) {
+        if (empty($preferences)) {
+            return false;
+        }
+        $preferencesArray = explode(',', $preferences);
+        if (count($preferencesArray) > 0) {
+            foreach ($preferencesArray as $pref) {
+                $s = strpos($pref, ';');
+                if ($s >= 2) {
+                    $code = substr($pref, 0, $s);
+                } else {
+                    $code = $pref;
+                }
+                $name = self::getLanguageFromIsocode($code);
+                if ($name !== false) {
+                    return $name;
+                }
+            }
+        }
+        return false;
+    }
 }

+ 1 - 1
main/admin/system_announcements.php

@@ -65,7 +65,7 @@ $show_announcement_list = true;
 $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : null;
 
 // Form was posted?
-if (isset ($_POST['action'])) {
+if (isset($_POST['action'])) {
     $action_todo = true;
 }
 

+ 4 - 4
main/admin/user_add.php

@@ -163,9 +163,9 @@ if (api_get_setting('login_is_email') != 'true') {
 $group = array();
 $auth_sources = 0; //make available wider as we need it in case of form reset (see below)
 $nb_ext_auth_source_added = 0;
-if (count($extAuthSource) > 0) {
+if (isset($extAuthSource) && count($extAuthSource) > 0) {
 	$auth_sources = array();
-	foreach($extAuthSource as $key => $info) {
+	foreach ($extAuthSource as $key => $info) {
 	    // @todo : make uniform external authentification configuration (ex : cas and external_login ldap)
 	    // Special case for CAS. CAS is activated from Chamilo > Administration > Configuration > CAS
 	    // extAuthSource always on for CAS even if not activated
@@ -291,9 +291,9 @@ if( $form->validate()) {
 		$picture        = $_FILES['picture'];
 		$platform_admin = intval($user['admin']['platform_admin']);
 		$send_mail      = intval($user['mail']['send_mail']);
-		$hr_dept_id     = intval($user['hr_dept_id']);
+		$hr_dept_id     = isset($user['hr_dept_id']) ? intval($user['hr_dept_id']) : 0;
 
-		if (count($extAuthSource) > 0 && $user['password']['password_auto'] == '2') {
+		if (isset($extAuthSource) && count($extAuthSource) > 0 && $user['password']['password_auto'] == '2') {
 			$auth_source = $user['password']['auth_source'];
 			$password = 'PLACEHOLDER';
 		} else {

+ 55 - 33
main/admin/user_edit.php

@@ -6,9 +6,7 @@
 
 // Language files that should be included
 $language_file = array('admin', 'registration');
-
 $cidReset = true;
-
 require_once '../inc/global.inc.php';
 
 $this_section = SECTION_PLATFORM_ADMIN;
@@ -91,8 +89,8 @@ $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
 $sql = "SELECT u.*, a.user_id AS is_admin FROM $table_user u LEFT JOIN $table_admin a ON a.user_id = u.user_id WHERE u.user_id = '".$user_id."'";
 $res = Database::query($sql);
 if (Database::num_rows($res) != 1) {
-	header('Location: user_list.php');
-	exit;
+    header('Location: user_list.php');
+    exit;
 }
 
 $user_data = Database::fetch_array($res, 'ASSOC');
@@ -182,25 +180,25 @@ if (api_get_setting('login_is_email') != 'true') {
 // Password
 $form->addElement('radio', 'reset_password', get_lang('Password'), get_lang('DontResetPassword'), 0);
 $nb_ext_auth_source_added = 0;
-if (count($extAuthSource) > 0) {
-	$auth_sources = array();
-	foreach($extAuthSource as $key => $info) {
-	    // @todo : make uniform external authentification configuration (ex : cas and external_login ldap)
-	    // Special case for CAS. CAS is activated from Chamilo > Administration > Configuration > CAS
-	    // extAuthSource always on for CAS even if not activated
-	    // same action for file user_add.php
-	    if (($key == CAS_AUTH_SOURCE && api_get_setting('cas_activate') === 'true') || ($key != CAS_AUTH_SOURCE)) {
-    		$auth_sources[$key] = $key;
-    		$nb_ext_auth_source_added++;
-	    }
-	}
-	if ($nb_ext_auth_source_added > 0) {
-	    // @todo check the radio button for external authentification and select the external authentification in the menu
-	    $group[] =$form->createElement('radio', 'reset_password', null, get_lang('ExternalAuthentication').' ', 3);
-	    $group[] =$form->createElement('select', 'auth_source', null, $auth_sources);
-	    $group[] =$form->createElement('static', '', '', '<br />');
-	    $form->addGroup($group, 'password', null, '', false);
-	}
+if (isset($extAuthSource) && !empty($extAuthSource) && count($extAuthSource) > 0) {
+    $auth_sources = array();
+    foreach ($extAuthSource as $key => $info) {
+        // @todo : make uniform external authentification configuration (ex : cas and external_login ldap)
+        // Special case for CAS. CAS is activated from Chamilo > Administration > Configuration > CAS
+        // extAuthSource always on for CAS even if not activated
+        // same action for file user_add.php
+        if (($key == CAS_AUTH_SOURCE && api_get_setting('cas_activate') === 'true') || ($key != CAS_AUTH_SOURCE)) {
+            $auth_sources[$key] = $key;
+            $nb_ext_auth_source_added++;
+        }
+    }
+    if ($nb_ext_auth_source_added > 0) {
+        // @todo check the radio button for external authentification and select the external authentification in the menu
+        $group[] = $form->createElement('radio', 'reset_password', null, get_lang('ExternalAuthentication').' ', 3);
+        $group[] = $form->createElement('select', 'auth_source', null, $auth_sources);
+        $group[] = $form->createElement('static', '', '', '<br />');
+        $form->addGroup($group, 'password', null, '', false);
+    }
 }
 $form->addElement('radio', 'reset_password', null, get_lang('AutoGeneratePassword'), 1);
 $group = array();
@@ -254,8 +252,8 @@ $form->addElement('select_language', 'language', get_lang('Language'));
 
 // Send email
 $group = array();
-$group[] =$form->createElement('radio', 'send_mail', null, get_lang('Yes'), 1);
-$group[] =$form->createElement('radio', 'send_mail', null, get_lang('No'), 0);
+$group[] = $form->createElement('radio', 'send_mail', null, get_lang('Yes'), 1);
+$group[] = $form->createElement('radio', 'send_mail', null, get_lang('No'), 0);
 $form->addGroup($group, 'mail', get_lang('SendMailToNewUser'), '&nbsp;', false);
 
 // Registration User and Date
@@ -325,11 +323,12 @@ if ($form->validate()) {
 	if ($user['status'] == DRH && $is_user_subscribed_in_course) {
 		$error_drh = true;
 	} else {
+        $userInfo = api_get_user_info($user_id);
 		$picture_element = $form->getElement('picture');
 		$picture = $picture_element->getValue();
 
 		$picture_uri = $user_data['picture_uri'];
-		if ($user['delete_picture']) {
+		if (isset($user['delete_picture']) && $user['delete_picture']) {
 			$picture_uri = UserManager::delete_user_picture($user_id);
 		} elseif (!empty($picture['name'])) {
 			$picture_uri = UserManager::update_user_picture($user_id, $_FILES['picture']['name'], $_FILES['picture']['tmp_name']);
@@ -338,18 +337,18 @@ if ($form->validate()) {
 		$lastname = $user['lastname'];
 		$firstname = $user['firstname'];
         $password = $user['password'];
-        $auth_source = $user['auth_source'];
-
+        $auth_source = isset($user['auth_source']) ? $user['auth_source'] : $userInfo['auth_source'];
 		$official_code = $user['official_code'];
 		$email = $user['email'];
 		$phone = $user['phone'];
-		$username = $user['username'];
+		$username = isset($user['username']) ? $user['username'] : $userInfo['username'];
 		$status = intval($user['status']);
 		$platform_admin = intval($user['platform_admin']);
 		$send_mail = intval($user['send_mail']);
 		$reset_password = intval($user['reset_password']);
-		$hr_dept_id = intval($user['hr_dept_id']);
+		$hr_dept_id = isset($user['hr_dept_id']) ? intval($user['hr_dept_id']) : null;
 		$language = $user['language'];
+
 		if ($user['radio_expiration_date'] == '1' && !$user_data['platform_admin']) {
             $expiration_date = return_datetime_from_array($user['expiration_date']);
 		} else {
@@ -366,12 +365,35 @@ if ($form->validate()) {
         if (api_get_setting('login_is_email') == 'true') {
             $username = $email;
         }
-		UserManager::update_user($user_id, $firstname, $lastname, $username, $password, $auth_source, $email, $status, $official_code, $phone, $picture_uri, $expiration_date, $active, null, $hr_dept_id, null, $language, null, $send_mail, $reset_password);
+
+        UserManager::update_user(
+            $user_id,
+            $firstname,
+            $lastname,
+            $username,
+            $password,
+            $auth_source,
+            $email,
+            $status,
+            $official_code,
+            $phone,
+            $picture_uri,
+            $expiration_date,
+            $active,
+            null,
+            $hr_dept_id,
+            null,
+            $language,
+            null,
+            $send_mail,
+            $reset_password
+        );
 
 		if (api_get_setting('openid_authentication') == 'true' && !empty($user['openid'])) {
 			$up = UserManager::update_openid($user_id,$user['openid']);
 		}
-		if ($user_id != $_SESSION['_uid']) {
+        $currentUserId = api_get_user_id();
+		if ($user_id != $currentUserId) {
 			if ($platform_admin == 1) {
                 UserManager::add_user_as_admin($user_id);
 			} else {
@@ -464,4 +486,4 @@ $content .= $form->return_form();
 $tpl = new Template($tool_name);
 $tpl->assign('message', $message);
 $tpl->assign('content', $content);
-$tpl->display_one_col_template();
+$tpl->display_one_col_template();

+ 1 - 1
main/admin/user_export.php

@@ -94,7 +94,7 @@ if ($form->validate()) {
 			$sql .= " FROM $user_table u ORDER BY lastname,firstname";
 		}
 		$filename = 'export_users_'.date('Y-m-d_H-i-s');
-	}	
+	}
 	$data = array();
 	$extra_fields = UserManager::get_extra_fields(0, 0, 5, 'ASC',false);
 	if ($export['addcsvheader']=='1' AND $export['file_type']=='csv') {

+ 7 - 6
main/admin/user_fields.php

@@ -157,6 +157,7 @@ if(1)
 	$table->set_header(7, get_lang('FieldChangeability'), false);
 	$table->set_header(8, get_lang('FieldFilter'), false);
 	$table->set_header(9, get_lang('Modify'), false);
+    $table->set_header(10, '', false);
 	$table->set_column_filter(5, 'order_filter');
 	$table->set_column_filter(6, 'modify_visibility');
 	$table->set_column_filter(7, 'modify_changeability');
@@ -210,14 +211,14 @@ function order_filter($field_order,$url_params,$row)
 	$return = '';
 	// the up icon only has to appear when the row can be moved up (all but the first row)
 	if ($row[5]<>1) {
-		$return .= '<a href="'.api_get_self().'?action=moveup&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'">'.Display::return_icon('up.gif', get_lang('Up')).'</a>';
+		$return .= '<a href="'.api_get_self().'?action=moveup&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'#'.$row[0].'">'.Display::return_icon('up.gif', get_lang('Up')).'</a>';
 	} else {
 		$return .= Display::return_icon('blank.gif','',array('width'=>'21px'));
 	}
 
 	// the down icon only has to appear when the row can be moved down (all but the last row)
 	if ($row[5]<>$number_of_extra_fields) {
-		$return .= '<a href="'.api_get_self().'?action=movedown&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'">'.Display::return_icon('down.gif', get_lang('Down')).'</a>';
+		$return .= '<a href="'.api_get_self().'?action=movedown&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'#'.$row[0].'">'.Display::return_icon('down.gif', get_lang('Down')).'</a>';
 	}
 	return $return;
 }
@@ -230,7 +231,7 @@ function order_filter($field_order,$url_params,$row)
  */
 function modify_visibility($visibility,$url_params,$row)
 {
-	return ($visibility?'<a href="'.api_get_self().'?action=hide_field&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'">'.Display::return_icon('visible.gif', get_lang('Hide')).'</a>':'<a href="'.api_get_self().'?action=show_field&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'">'.Display::return_icon('invisible.gif', get_lang('Show')).'</a>');
+	return ($visibility?'<a href="'.api_get_self().'?action=hide_field&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'#'.$row[0].'">'.Display::return_icon('visible.gif', get_lang('Hide')).'</a>':'<a href="'.api_get_self().'?action=show_field&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'#'.$row[0].'">'.Display::return_icon('invisible.gif', get_lang('Show')).'</a>');
 }
 /**
  * Modify the changeability field to show links and icons
@@ -241,13 +242,13 @@ function modify_visibility($visibility,$url_params,$row)
  */
 function modify_changeability($changeability,$url_params,$row)
 {
-	return ($changeability?'<a href="'.api_get_self().'?action=freeze_field&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'">'.Display::return_icon('right.gif', get_lang('MakeUnchangeable')).'</a>':'<a href="'.api_get_self().'?action=thaw_field&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'">'.Display::return_icon('wrong.gif', get_lang('MakeChangeable')).'</a>');
+	return ($changeability?'<a href="'.api_get_self().'?action=freeze_field&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'#'.$row[0].'">'.Display::return_icon('right.gif', get_lang('MakeUnchangeable')).'</a>':'<a href="'.api_get_self().'?action=thaw_field&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'#'.$row[0].'">'.Display::return_icon('wrong.gif', get_lang('MakeChangeable')).'</a>');
 }
 
 function modify_field_filter ($changeability,$url_params,$row)
 {
-	return ($changeability?'<a href="'.api_get_self().'?action=filter_off&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'">'.Display::return_icon('right.gif', get_lang('FilterOff')).'</a>':'' .
-						   '<a href="'.api_get_self().'?action=filter_on&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'">'.Display::return_icon('wrong.gif', get_lang('FilterOn')).'</a>');
+	return ($changeability?'<a href="'.api_get_self().'?action=filter_off&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'#'.$row[0].'">'.Display::return_icon('right.gif', get_lang('FilterOff')).'</a>':'' .
+						   '<a href="'.api_get_self().'?action=filter_on&field_id='.$row[0].'&sec_token='.$_SESSION['sec_token'].'#'.$row[0].'">'.Display::return_icon('wrong.gif', get_lang('FilterOn')).'</a>');
 }
 
 function edit_filter($id, $url_params, $row) {

+ 6 - 3
main/admin/user_import.php

@@ -122,6 +122,9 @@ function complete_missing_data($user)
     // 4. Set authsource if not allready set.
     if (empty($user['AuthSource'])) {
         $user['AuthSource'] = PLATFORM_AUTH_SOURCE;
+    }
+     if (empty($user['ExpiryDate'])) {
+        $user['ExpiryDate'] = '0000-00-00 00:00:00';
     }
     return $user;
 }
@@ -158,7 +161,7 @@ function save_data($users)
                 $user['PhoneNumber'],
                 '',
                 $user['AuthSource'],
-                null,
+                $user['ExpiryDate'],
                 1,
                 0,
                 null,
@@ -466,8 +469,8 @@ if ($count_fields > 0) {
     <p><?php echo get_lang('CSVMustLookLike').' ('.get_lang('MandatoryFields').')'; ?> :</p>
     <blockquote>
 <pre>
-<b>LastName</b>;<b>FirstName</b>;<b>Email</b>;UserName;Password;AuthSource;OfficialCode;PhoneNumber;Status;<span style="color:red;"><?php if (count($list) > 0) echo implode(';', $list).';'; ?></span>Courses;ClassId;
-<b>xxx</b>;<b>xxx</b>;<b>xxx</b>;xxx;xxx;<?php echo implode('/', $defined_auth_sources); ?>;xxx;xxx;user/teacher/drh;<span style="color:red;"><?php if (count($list_reponse) > 0) echo implode(';', $list_reponse).';'; ?></span>xxx1|xxx2|xxx3;1;<br />
+<b>LastName</b>;<b>FirstName</b>;<b>Email</b>;UserName;Password;AuthSource;OfficialCode;PhoneNumber;Status;ExpiryDate;<span style="color:red;"><?php if (count($list) > 0) echo implode(';', $list).';'; ?></span>Courses;ClassId;
+<b>xxx</b>;<b>xxx</b>;<b>xxx</b>;xxx;xxx;<?php echo implode('/', $defined_auth_sources); ?>;xxx;xxx;user/teacher/drh;0000-00-00 00:00:00;<span style="color:red;"><?php if (count($list_reponse) > 0) echo implode(';', $list_reponse).';'; ?></span>xxx1|xxx2|xxx3;1;<br />
 </pre>
 </blockquote>
 <p><?php echo get_lang('XMLMustLookLike').' ('.get_lang('MandatoryFields').')'; ?> :</p>

+ 0 - 5
main/admin/user_information.php

@@ -23,10 +23,6 @@ if (!isset($_GET['user_id'])) {
 }
 $user = api_get_user_info($_GET['user_id']);
 $tool_name = $user['complete_name'].(empty($user['official_code'])?'':' ('.$user['official_code'].')');
-
-/**
- * Display
- */
 Display::display_header($tool_name);
 
 $table_course_user = Database :: get_main_table(TABLE_MAIN_COURSE_USER);
@@ -121,7 +117,6 @@ if (count($sessions) > 0) {
     $header[] = array (get_lang('Status'), true);
     $header[] = array ('', false);
 
-
     foreach ($sessions as $session_item) {
 
         $data = array ();

+ 2 - 4
main/admin/user_move_stats.php

@@ -188,8 +188,7 @@ if (isset($_REQUEST['load_ajax'])) {
                             }
                         }                                       
                     }                
-                }
-                
+                }                
                 
                 //2.track_e_attempt, track_e_attempt_recording, track_e_downloads
                 //Nothing to do because there are not relationship with a session
@@ -235,8 +234,7 @@ if (isset($_REQUEST['load_ajax'])) {
                     }  
                 }                
                 
-                //5. lp_item_view           
-                 
+                //5. lp_item_view
                //CHECK ORIGIN
                
                 $sql = "SELECT * FROM $TBL_LP_VIEW WHERE user_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id ";

+ 1 - 0
main/admin/usergroup_user_import.php

@@ -150,6 +150,7 @@ $form->addElement('file', 'import_file', get_lang('ImportCSVFileLocation'));
 $form->addElement('checkbox', 'unsubscribe', '', get_lang('UnsubscribeUserIfSubscriptionIsNotInFile'));
 $form->addElement('style_submit_button', 'submit', get_lang('Import'), 'class="save"');
 
+$errors = array();
 if ($form->validate()) {
     $users_classes = parse_csv_data($_FILES['import_file']['tmp_name']);
     $errors = validate_data($users_classes);

+ 1 - 5
main/admin/usergroups.php

@@ -173,10 +173,7 @@ if (isset($_GET['action']) && $_GET['action'] == 'add') {
         $form->setConstants(array('sec_token' => $token));
         $form->display();
     }
-}
-
-// Action handling: deleting a note
-elseif (isset($_GET['action']) && $_GET['action'] == 'delete' && is_numeric($_GET['id'])) {
+} elseif (isset($_GET['action']) && $_GET['action'] == 'delete' && is_numeric($_GET['id'])) {
     $res = $usergroup->delete(Security::remove_XSS($_GET['id']));
     if ($res) {
         Display::display_confirmation_message(get_lang('Deleted'));
@@ -185,5 +182,4 @@ elseif (isset($_GET['action']) && $_GET['action'] == 'delete' && is_numeric($_GE
 } else {
     $usergroup->display();
 }
-
 Display :: display_footer();

+ 1 - 1
main/announcements/announcements.inc.php

@@ -254,7 +254,7 @@ class AnnouncementManager
                 echo "<tr><td>";
                 $realname = $attachment_list['path'];
                 $user_filename = $attachment_list['filename'];
-                $full_file_name = 'download.php?file=' . $realname;
+                $full_file_name = 'download.php?'.api_get_cidreq().'&file=' . $realname;
                 echo '<br/>';
                 echo Display::return_icon('attachment.gif', get_lang('Attachment'));
                 echo '<a href="' . $full_file_name . ' "> ' . $user_filename . ' </a>';

+ 11 - 12
main/announcements/announcements.php

@@ -759,12 +759,11 @@ if ($display_form) {
 		echo '<script>toggle_sendto();</script>';
 	}
 
-} // displayform
-
-/*
-		DISPLAY ANNOUNCEMENT LIST
-*/
+} // end of displayform for announcement creation/edition
 
+/**
+ * Announcements list display
+ */
 $course_id = api_get_course_int_id();
 
 //if ($display_announcement_list && !$surveyid) {
@@ -962,7 +961,7 @@ if ($display_announcement_list) {
     	$iterator = 1;
     	$bottomAnnouncement = $announcement_number;
 
-    	echo '<table width="100%" class="data_table">';
+    	echo '<table width="100%" class="data_table announcements-list">';
         $ths = Display::tag('th', get_lang('Title'));
         $ths .= Display::tag('th', get_lang('By') );
         $ths .= Display::tag('th', get_lang('LastUpdateDate') );
@@ -997,7 +996,7 @@ if ($display_announcement_list) {
     				$style = '';
     			}
 
-    			echo "<tr>";
+    			echo '<tr class="announcements-list-line">';
 
     		    // show attachment list
                 $attachment_list = array();
@@ -1009,14 +1008,14 @@ if ($display_announcement_list) {
                 }
 
                 /* TITLE */
-    		    $title = Display::url($title.$attachment_icon, '?action=view&id='.$myrow['id']);
-                echo Display::tag('td', Security::remove_XSS($title), array('class' => $style));
+    		    $title = Display::url($title.$attachment_icon, api_get_self().'?'.api_get_cidreq().'&action=view&id='.$myrow['id']);
+                echo Display::tag('td', Security::remove_XSS($title), array('class' => 'announcements-list-line-title '.$style));
 
                 $user_info		= api_get_user_info($myrow['insert_user_id']);
                 $username       = sprintf(get_lang("LoginX"), $user_info['username']);
                 $username_span  = Display::tag('span', api_get_person_name($user_info['firstName'], $user_info['lastName']), array('title'=>$username));
-    			echo Display::tag('td', $username_span);
-                echo Display::tag('td', api_convert_and_format_date($myrow['insert_date'], DATE_TIME_FORMAT_LONG));
+    			echo Display::tag('td', $username_span, array('class' => 'announcements-list-line-by-user'));
+                echo Display::tag('td', api_convert_and_format_date($myrow['insert_date'], DATE_TIME_FORMAT_LONG), array('class' => 'announcements-list-line-datetime'));
 
     			// we can edit if : we are the teacher OR the element belongs to the session we are coaching OR the option to allow users to edit is on
     			$modify_icons = '';
@@ -1051,7 +1050,7 @@ if ($display_announcement_list) {
                             "</a>";
                     }
     				$iterator ++;
-    				echo Display::tag('td', $modify_icons);
+    				echo Display::tag('td', $modify_icons, array('class' => 'announcements-list-line-actions'));
     			}
     			echo "</tr>";
     		}

+ 0 - 3
main/attendance/attendance_controller.php

@@ -163,7 +163,6 @@
 	 */
 	public function attendance_delete($attendance_id) {
 		$attendance = new Attendance();
-		//$attendance_id = intval($attendance_id);
 		if (!empty($attendance_id)) {
 			$affected_rows = $attendance->attendance_delete($attendance_id);
 		}
@@ -179,7 +178,6 @@
 	 */
 	public function attendance_restore($attendance_id) {
 		$attendance = new Attendance();
-		//$attendance_id = intval($attendance_id);
 		if (!empty($attendance_id)) {
 			$affected_rows = $attendance->attendance_restore($attendance_id);
 		}
@@ -488,7 +486,6 @@
         }
         $max_cols_per_page = 12; //10 dates + 2 name and number
         $max_dates_per_page = $max_dates_per_page_original = $max_cols_per_page - 2;//10
-        //var_dump($cols);exit;
         $rows = count($data_table);
 
         if ($cols > $max_cols_per_page) {

+ 1 - 1
main/auth/cas/lib/CAS/client.php

@@ -920,7 +920,7 @@ class CASClient
 				$_SESSION['phpCAS']['unauth_count'] = 0;
 				$_SESSION['phpCAS']['auth_checked'] = true;
 				phpCAS::trace('user is not authenticated (cache reset)');
-				$this->redirectToCas(TRUE/* gateway */);	
+				// $this->redirectToCas(TRUE/* gateway */);
 				// never reached
 				$res = FALSE;
 			}

+ 108 - 30
main/auth/inscription.php

@@ -334,40 +334,66 @@ if (api_get_setting('allow_terms_conditions') == 'true') {
 $form->addElement('button', 'submit', get_lang('RegisterUser'), array('class' => 'btn btn-primary btn-large'));
 
 if ($form->validate()) {
-
-    //$values = $form->exportValues();
     $values = $form->getSubmitValues(1);
-    $values['username'] = api_substr($values['username'], 0, USERNAME_MAX_LENGTH); //make *sure* the login isn't too long
+    //make *sure* the login isn't too long
+    $values['username'] = api_substr($values['username'], 0, USERNAME_MAX_LENGTH);
 
     if (api_get_setting('allow_registration_as_teacher') == 'false') {
         $values['status'] = STUDENT;
     }
+
     // Added by Ivan Tcholakov, 06-MAR-2008.
     if (empty($values['official_code'])) {
-        $values['official_code'] =  api_strtoupper($values['username']);
+        $values['official_code'] = api_strtoupper($values['username']);
     }
 
     if (api_get_setting('login_is_email') == 'true') {
         $values['username'] = $values['email'];
     }
 
-    if ($user_already_registered_show_terms && api_get_setting('allow_terms_conditions') == 'true') {
+    if ($user_already_registered_show_terms &&
+        api_get_setting('allow_terms_conditions') == 'true'
+    ) {
         $user_id = $_SESSION['term_and_condition']['user_id'];
         $is_admin = UserManager::is_admin($user_id);
         Session::write('is_platformAdmin', $is_admin);
     } else {
+
         // Creates a new user
-        $user_id = UserManager::create_user($values['firstname'], $values['lastname'], $values['status'], $values['email'], $values['username'], $values['pass1'], $values['official_code'], $values['language'], $values['phone'], $picture_uri, PLATFORM_AUTH_SOURCE, null, 1, 0, null, null, true);
+        $user_id = UserManager::create_user(
+            $values['firstname'],
+            $values['lastname'],
+            $values['status'],
+            $values['email'],
+            $values['username'],
+            $values['pass1'],
+            $values['official_code'],
+            $values['language'],
+            $values['phone'],
+            null,
+            PLATFORM_AUTH_SOURCE,
+            null,
+            1,
+            0,
+            null,
+            null,
+            true
+        );
 
         // Register extra fields
         $extras = array();
         foreach ($values as $key => $value) {
-            if (substr($key, 0, 6) == 'extra_') { //an extra field
-                $extras[substr($key,6)] = $value;
+            if (substr($key, 0, 6) == 'extra_') {
+                //an extra field
+                $extras[substr($key, 6)] = $value;
             } elseif (strpos($key, 'remove_extra_') !== false) {
                 $extra_value = Security::filter_filename(urldecode(key($value)));
                 // To remove from user_field_value and folder
-                UserManager::update_extra_field_value($user_id, substr($key,13), $extra_value);
+                UserManager::update_extra_field_value(
+                    $user_id,
+                    substr($key,13),
+                    $extra_value
+                );
             }
         }
 
@@ -396,7 +422,7 @@ if ($form->validate()) {
         }
 
         if ($user_id) {
-            // storing the extended profile
+            // Storing the extended profile
             $store_extended = false;
             $sql = "UPDATE ".Database::get_main_table(TABLE_MAIN_USER)." SET ";
             if (api_get_setting('extended_profile') == 'true' && api_get_setting('extendedprofile_registration', 'mycomptetences') == 'true') {
@@ -427,15 +453,38 @@ if ($form->validate()) {
                 Database::query($sql);
             }
 
-            // if the account has to be approved then we set the account to inactive, sent a mail to the platform admin and exit the page.
+            $course_code_redirect = Session::read('course_redirect');
+
+            // Saving user to course if it was set.
+            if (!empty($course_code_redirect)) {
+                $course_info = api_get_course_info($course_code_redirect);
+                if (!empty($course_info)) {
+                    if (in_array(
+                        $course_info['visibility'],
+                        array(
+                            COURSE_VISIBILITY_OPEN_PLATFORM,
+                            COURSE_VISIBILITY_OPEN_WORLD
+                        )
+                    )
+                    ) {
+                        CourseManager::subscribe_user(
+                            $user_id,
+                            $course_info['code']
+                        );
+                    }
+                }
+            }
+
+            /* If the account has to be approved then we set the account to inactive,
+            sent a mail to the platform admin and exit the page.*/
+
             if (api_get_setting('allow_registration') == 'approval') {
                 $TABLE_USER = Database::get_main_table(TABLE_MAIN_USER);
                 // 1. set account inactive
-                $sql = "UPDATE ".$TABLE_USER."	SET active='0' WHERE user_id='".$user_id."'";
+                $sql = "UPDATE $TABLE_USER SET active='0' WHERE user_id = ".$user_id;
                 Database::query($sql);
 
                 // 2. Send mail to all platform admin
-
                 $emailsubject	 = get_lang('ApprovalForNewAccount',null,$values['language']).': '.$values['username'];
                 $emailbody		 = get_lang('ApprovalForNewAccount',null,$values['language'])."\n";
                 $emailbody		.= get_lang('UserName',null,$values['language']).': '.$values['username']."\n";
@@ -449,12 +498,28 @@ if ($form->validate()) {
                 }
                 $emailbody		.= get_lang('Email',null,$values['language']).': '.$values['email']."\n";
                 $emailbody		.= get_lang('Status',null,$values['language']).': '.$values['status']."\n\n";
-                $url_edit        = Display::url(api_get_path(WEB_CODE_PATH).'admin/user_edit.php?user_id='.$user_id, api_get_path(WEB_CODE_PATH).'admin/user_edit.php?user_id='.$user_id);
+
+                $url_edit = Display::url(
+                    api_get_path(WEB_CODE_PATH).'admin/user_edit.php?user_id='.$user_id,
+                    api_get_path(WEB_CODE_PATH).'admin/user_edit.php?user_id='.$user_id
+                );
+
                 $emailbody		.= get_lang('ManageUser',null,$values['language']).": $url_edit";
 
                 $admins = UserManager::get_all_administrators();
                 foreach ($admins as $admin_info) {
-                    MessageManager::send_message($admin_info['user_id'], $emailsubject, $emailbody, null, null, null, null, null, null, $user_id);
+                    MessageManager::send_message(
+                        $admin_info['user_id'],
+                        $emailsubject,
+                        $emailbody,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        $user_id
+                    );
                 }
 
                 // 3. exit the page
@@ -471,8 +536,7 @@ if ($form->validate()) {
 
     // Terms & Conditions
     if (api_get_setting('allow_terms_conditions') == 'true') {
-        // update the terms & conditions
-
+        // Update the terms & conditions.
         if (isset($values['legal_accept_type'])) {
             $cond_array = explode(':', $values['legal_accept_type']);
             if (!empty($cond_array[0]) && !empty($cond_array[1])) {
@@ -497,7 +561,7 @@ if ($form->validate()) {
     Session::write('_user', $_user);
     Session::write('is_allowedCreateCourse', $is_allowedCreateCourse);
 
-    //stats
+    // Stats
     event_login();
 
     // last user login date is now
@@ -507,9 +571,11 @@ if ($form->validate()) {
 
     $text_after_registration = '<p>'.get_lang('Dear', null, $_user['language']).' '.stripslashes(Security::remove_XSS($recipient_name)).',<br /><br />'.get_lang('PersonalSettings',null,$_user['language']).".</p>";
 
-    $form_data = array( 'button' => Display::button('next', get_lang('Next', null, $_user['language']), array('class' => 'btn btn-primary btn-large')),
-                        'message' => null,
-                        'action' => api_get_path(WEB_PATH).'user_portal.php');
+    $form_data = array(
+        'button' => Display::button('next', get_lang('Next', null, $_user['language']), array('class' => 'btn btn-primary btn-large')),
+        'message' => null,
+        'action' => api_get_path(WEB_PATH).'user_portal.php'
+    );
 
     if (api_get_setting('allow_terms_conditions') == 'true' && $user_already_registered_show_terms) {
         $form_data['action'] = api_get_path(WEB_PATH).'user_portal.php';
@@ -547,7 +613,8 @@ if ($form->validate()) {
      *
      * You can send to your students an URL like this
      * http://chamilodev.beeznest.com/main/auth/inscription.php?c=ABC&e=3
-     * Where "c" is the course code and "e" is the exercise Id, after a succesfull registration the user will be sent to the course or exercise
+     * Where "c" is the course code and "e" is the exercise Id, after a successful
+     * registration the user will be sent to the course or exercise
      *
      */
     $course_code_redirect = Session::read('course_redirect');
@@ -555,23 +622,34 @@ if ($form->validate()) {
     if (!empty($course_code_redirect)) {
         $course_info = api_get_course_info($course_code_redirect);
         if (!empty($course_info)) {
-
-            if (in_array($course_info['visibility'], array(COURSE_VISIBILITY_OPEN_PLATFORM, COURSE_VISIBILITY_OPEN_WORLD))) {
+            if (in_array($course_info['visibility'],
+                array(COURSE_VISIBILITY_OPEN_PLATFORM, COURSE_VISIBILITY_OPEN_WORLD))
+            ) {
                 $user_id = api_get_user_id();
-                if (CourseManager::subscribe_user($user_id, $course_info['code'])) {
+                if (CourseManager::is_user_subscribed_in_course($user_id, $course_info['code'])) {
 
                     $form_data['action'] = $course_info['course_public_url'];
                     $form_data['message'] = sprintf(get_lang('YouHaveBeenRegisteredToCourseX'), $course_info['title']);
-                    $form_data['button'] = Display::button('next', get_lang('GoToCourse', null, $_user['language']), array('class' => 'btn btn-primary btn-large'));
+                    $form_data['button'] = Display::button(
+                        'next',
+                        get_lang('GoToCourse', null, $_user['language']),
+                        array('class' => 'btn btn-primary btn-large')
+                    );
 
                     $exercise_redirect = intval(Session::read('exercise_redirect'));
                     $objExercise = new Exercise();
-                    $result = $objExercise->read($exercise_id);
+                    $result = $objExercise->read($exercise_redirect);
+
                     if (!empty($exercise_redirect) && !empty($result)) {
-                        $form_data['action'] = api_get_path(WEB_CODE_PATH).'exercice/overview.php?exerciseId='.intval($exercise_redirect).'&cidReq='.$course_info['code'];
+                        $form_data['action'] = api_get_path(WEB_CODE_PATH).'exercice/overview.php?exerciseId='.$exercise_redirect.'&cidReq='.$course_info['code'];
                         $form_data['message'] .= '<br />'.get_lang('YouCanAccessTheExercise');
-                        $form_data['button'] = Display::button('next', get_lang('Go', null, $_user['language']), array('class' => 'btn btn-primary btn-large'));
+                        $form_data['button'] = Display::button(
+                            'next',
+                            get_lang('Go', null, $_user['language']),
+                            array('class' => 'btn btn-primary btn-large')
+                        );
                     }
+
                     if (!empty($form_data['action'])) {
                         header('Location: '.$form_data['action']);
                         exit;
@@ -592,7 +670,7 @@ if ($form->validate()) {
     }
     $text_after_registration .= $form_register->return_form();
 
-    //Just in case
+    // Just in case
     Session::erase('course_redirect');
     Session::erase('exercise_redirect');
 

+ 21 - 16
main/auth/lostPassword.php

@@ -24,19 +24,19 @@ require_once '../inc/global.inc.php';
 require_once api_get_path(LIBRARY_PATH).'mail.lib.inc.php';
 
 // Custom pages
-// Had to move the form handling in here, because otherwise there would 
+// Had to move the form handling in here, because otherwise there would
 // already be some display output.
 global $_configuration;
 
 if (CustomPages::enabled()) {
     //Reset Password when user goes to the link
-    if ($_GET['reset'] && $_GET['id']){
+    if ($_GET['reset'] && $_GET['id']) {
         $mesg = Login::reset_password($_GET["reset"], $_GET["id"], true);
         CustomPages::display(CustomPages::INDEX_UNLOGGED, array('info' => $mesg));
     }
 
     //Check email/username and do the right thing
-    if (isset ($_POST['user']) && isset ($_POST['email'])) {
+    if (isset($_POST['user']) && isset ($_POST['email'])) {
         $user = $_POST['user'];
         $email = $_POST['email'];
 
@@ -57,16 +57,18 @@ if (CustomPages::enabled()) {
 
         if ($result && $num_rows > 0) {
             if ($num_rows > 1) {
-                $by_username = false; // more than one user
+                // more than one user
+                $by_username = false;
                 while ($data = Database::fetch_array($result)) {
                     $user[] = $data;
                 }
             } else {
-                $by_username = true; // single user (valid user + email)
+                // single user (valid user + email)
+                $by_username = true;
                 $user = Database::fetch_array($result);
             }
             if ($_configuration['password_encryption'] != 'none') {
-                //Send email with secret link to user
+                // Send email with secret link to user
                 Login::handle_encrypted_password($user, $by_username);
             } else {
                 Login::send_password_to_user($user, $by_username);
@@ -75,9 +77,12 @@ if (CustomPages::enabled()) {
             CustomPages::display(CustomPages::LOST_PASSWORD, array('error' => get_lang('NoUserAccountWithThisEmailAddress')));
         }
     } else {
-        CustomPages::display(CustomPages::LOGGED_OUT);
+        CustomPages::display(CustomPages::LOST_PASSWORD);
     }
-    CustomPages::display(CustomPages::INDEX_UNLOGGED, array('info' => get_lang('YourPasswordHasBeenEmailed')));
+    CustomPages::display(
+        CustomPages::INDEX_UNLOGGED,
+        array('info' => get_lang('YourPasswordHasBeenEmailed'))
+    );
 }
 
 $tool_name = get_lang('LostPassword');
@@ -91,9 +96,9 @@ if (api_get_setting('allow_lostpassword') == 'false') {
 	api_not_allowed();
 }
 
-if (isset($_GET['reset']) && isset($_GET['id'])) {	
-    $message = Display::return_message(Login::reset_password($_GET["reset"], $_GET["id"], true), 'normal', false);    
-	$message .= '<a href="'.api_get_path(WEB_CODE_PATH).'auth/lostPassword.php" class="btn" >'.get_lang('Back').'</a>';	
+if (isset($_GET['reset']) && isset($_GET['id'])) {
+    $message = Display::return_message(Login::reset_password($_GET["reset"], $_GET["id"], true), 'normal', false);
+	$message .= '<a href="'.api_get_path(WEB_CODE_PATH).'auth/lostPassword.php" class="btn" >'.get_lang('Back').'</a>';
 	echo $message;
 } else {
 	$form = new FormValidator('lost_password');
@@ -107,11 +112,11 @@ if (isset($_GET['reset']) && isset($_GET['id'])) {
 
 	if ($form->validate()) {
 		$values = $form->exportValues();
-        
+
         $users_related_to_username = Login::get_user_accounts_by_username($values['user']);
-		
+
 		if ($users_related_to_username) {
-            $by_username = true;            
+            $by_username = true;
             foreach ($users_related_to_username as $user) {
                 if ($_configuration['password_encryption'] != 'none') {
                     Login::handle_encrypted_password($user, $by_username);
@@ -120,9 +125,9 @@ if (isset($_GET['reset']) && isset($_GET['id'])) {
                 }
             }
 		} else {
-			Display::display_warning_message(get_lang('NoUserAccountWithThisEmailAddress'));			
+			Display::display_warning_message(get_lang('NoUserAccountWithThisEmailAddress'));
 		}
-	} else {						
+	} else {
 		$form->display();
 	}
 }

+ 7 - 7
main/auth/my_progress.php

@@ -22,7 +22,6 @@ $nameTools = get_lang('MyProgress');
 api_block_anonymous_users();
 
 $htmlHeadXtra[] = api_get_js('jquery.timelinr-0.9.5.js');
-
 $htmlHeadXtra[] = '
 <script language="javascript">
 $(function() {
@@ -48,7 +47,6 @@ $(function() {
 require_once api_get_path(SYS_CODE_PATH).'mySpace/myspace.lib.php';
 
 $user_id = api_get_user_id();
-
 $course_user_list = CourseManager::get_courses_list_by_user_id($user_id);
 $dates = $issues = '';
 
@@ -74,13 +72,17 @@ if (!empty($course_user_list)) {
                         <div class="row">
                             <div class="span2"><div class="thumbnail">'.$course_image.'</div>
                         </div>
-                        <div class="span3">'.sprintf(get_lang('YouHaveEnteredTheCourseXInY'), $result['course_code'], api_convert_and_format_date($login, DATE_FORMAT_LONG)).'</div>
+                        <div class="span3">'.sprintf(
+                            get_lang('YouHaveEnteredTheCourseXInY'),
+                            $result['course_code'],
+                            api_convert_and_format_date($login, DATE_FORMAT_LONG)
+                        ).'</div>
                     </li>';
         $count++;
     }
 }
 
-$content .= Tracking::show_user_progress(api_get_user_id());
+$content .= Tracking::show_user_progress(api_get_user_id(), $_GET['session_id']);
 $content .= Tracking::show_course_detail(api_get_user_id(), $_GET['course'], $_GET['session_id']);
 
 if (!empty($dates)) {
@@ -88,13 +90,11 @@ if (!empty($dates)) {
         $content .= '<br /><br />';
     }
     $content .= '<div class="row"><div class="span12">'.Display::page_subheader(get_lang('Timeline')).'</div>';
-
     $content .= '<div id="my_timeline">
         <div class="actions">
             <a href="#" id="prev"></a> <!-- optional -->
             <a href="#" id="next"></a> <!-- optional -->
         </div>
-
     <ul id="dates">
         '.$dates.'
     </ul>
@@ -112,4 +112,4 @@ $tpl = new Template($tool_name);
 
 $tpl->assign('message', $message);
 $tpl->assign('content', $content);
-$tpl->display_one_col_template();
+$tpl->display_one_col_template();

+ 0 - 1
main/auth/profile.php

@@ -301,7 +301,6 @@ if (is_profile_editable()) {
 } else {
     $form->freeze();
 }
-
 $user_data = array_merge($user_data, $extra_data);
 $form->setDefaults($user_data);
 

+ 56 - 25
main/auth/set_temp_password.php

@@ -25,7 +25,8 @@ class sso {
     /**
      * Instanciates the object, initializing all relevant URL strings
      */
-    public function __construct() {
+    public function __construct()
+    {
         $this->protocol   = api_get_setting('sso_authentication_protocol');
         // There can be multiple domains, so make sure to take only the first
         // This might be later extended with a decision process
@@ -43,7 +44,8 @@ class sso {
     /**
      * Unlogs the user from the remote server 
      */
-    public function logout() {
+    public function logout()
+    {
         header('Location: '.$this->deauth_url);
         exit;
     }
@@ -51,8 +53,13 @@ class sso {
     /**
      * Sends the user to the master URL for a check of active connection
      */
-    public function ask_master() {
-        $params = 'sso_referer='.urlencode($this->referer).'&sso_target='.urlencode($this->target);
+    public function ask_master()
+    {
+        $tempKey = api_generate_password(32);
+        $params = 'sso_referer='.urlencode($this->referer).
+            '&sso_target='.urlencode($this->target).
+            '&sso_challenge='.$tempKey;
+        Session::write('tempkey', $tempKey);
         if (strpos($this->master_url, "?") === false) {
             $params = "?$params";
         } else {
@@ -66,7 +73,8 @@ class sso {
      * Validates the received active connection data with the database
      * @return	bool	Return the loginFailed variable value to local.inc.php
      */
-    public function check_user() {
+    public function check_user()
+    {
         global $_user;
         $loginFailed = false;
         //change the way we recover the cookie depending on how it is formed
@@ -86,8 +94,7 @@ class sso {
             $uData = Database::fetch_array($result);
             //Check the user's password
             if ($uData['auth_source'] == PLATFORM_AUTH_SOURCE) {
-             
-                //the authentification of this user is managed by Chamilo itself
+                //This user's authentification is managed by Chamilo itself
                 // check the user's password
                 // password hash comes already parsed in sha1, md5 or none
                 
@@ -97,21 +104,35 @@ class sso {
                 error_log($sso['username']);
                 error_log($uData['username']);
                 */
-                
-                if ($sso['secret'] === sha1($uData['password']) 
-                    && ($sso['username'] == $uData['username'])) {
-                    error_log('user n password are ok');                    
+                global $_configuration;
+                // Two possible authentication methods here: legacy using password
+                // and new using a temporary, session-fixed, tempkey
+                if (($sso['username'] == $uData['username']
+                        && $sso['secret'] === sha1(
+                            $uData['username'].
+                            Session::read('tempkey').
+                            $_configuration['security_key']
+                        )
+                    )
+                    or (
+                        ($sso['secret'] === sha1($uData['password']))
+                        && ($sso['username'] == $uData['username'])
+                    )
+                ) {
+                    //error_log('user n password are ok');
                     //Check if the account is active (not locked)
                     if ($uData['active']=='1') {
                         // check if the expiration date has not been reached
-                        if ($uData['expiration_date'] > date('Y-m-d H:i:s') OR $uData['expiration_date']=='0000-00-00 00:00:00') {                            
+                        if ($uData['expiration_date'] > date('Y-m-d H:i:s')
+                            or $uData['expiration_date']=='0000-00-00 00:00:00') {
                             
                             //If Multiple URL is enabled
                             if (api_get_multiple_access_url()) {
-                                //Check the access_url configuration setting if the user is registered in the access_url_rel_user table
+                                //Check the access_url configuration setting if
+                                // the user is registered in the access_url_rel_user table
                                 //Getting the current access_url_id of the platform
                                 $current_access_url_id = api_get_current_access_url_id();
-                                // my user is subscribed in these 
+                                // my user is subscribed in these
                                 //sites: $my_url_list
                                 $my_url_list = api_get_access_url_from_user($uData['user_id']);
                             } else {
@@ -122,7 +143,7 @@ class sso {
                             $my_user_is_admin = UserManager::is_admin($uData['user_id']);
                             
                             if ($my_user_is_admin === false) {
-                                if (is_array($my_url_list) && count($my_url_list) > 0 ) {
+                                if (is_array($my_url_list) && count($my_url_list) > 0) {
                                     if (in_array($current_access_url_id, $my_url_list)) {
                                         // the user has permission to enter at this site
                                         $_user['user_id'] = $uData['user_id'];
@@ -141,7 +162,7 @@ class sso {
                                         exit;
                                     }
                                 } else {
-                                    // there is no URL in the multiple 
+                                    // there is no URL in the multiple
                                     // urls list for this user
                                     $loginFailed = true;
                                     Session::erase('_uid');
@@ -151,8 +172,8 @@ class sso {
                             } else {
                                 //Only admins of the "main" (first) Chamilo
                                 // portal can login wherever they want
-                                if (in_array(1, $my_url_list)) { 
-                                    //Check if this admin is admin on the  
+                                if (in_array(1, $my_url_list)) {
+                                    //Check if this admin is admin on the
                                     // principal portal
                                     $_user['user_id'] = $uData['user_id'];
                                     $_user = api_get_user_info($_user['user_id']);
@@ -161,26 +182,32 @@ class sso {
                                     Session::write('_user', $_user);
                                     event_login();
                                 } else {
-                                    //Secondary URL admin wants to login 
+                                    //Secondary URL admin wants to login
                                     // so we check as a normal user
                                     if (in_array($current_access_url_id, $my_url_list)) {
                                         $_user['user_id'] = $uData['user_id'];
                                         $_user = api_get_user_info($_user['user_id']);
-                                        Session::write('_user',$_user);
+                                        Session::write('_user', $_user);
                                         event_login();
                                     } else {
                                         $loginFailed = true;
                                         Session::erase('_uid');
-                                        header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
+                                        header(
+                                            'Location: '.api_get_path(WEB_PATH)
+                                            .'index.php?loginFailed=1&error=access_url_inactive'
+                                        );
                                         exit;
                                     }
                                 }
-                            }                       
+                            }
                         } else {
                             // user account expired
                             $loginFailed = true;
                             Session::erase('_uid');
-                            header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=account_expired');
+                            header(
+                                'Location: '.api_get_path(WEB_PATH)
+                                .'index.php?loginFailed=1&error=account_expired'
+                            );
                             exit;
                         }
                     } else {
@@ -201,7 +228,10 @@ class sso {
                 //Auth_source is wrong
                 $loginFailed = true;
                 Session::erase('_uid');
-                header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=wrong_authentication_source');
+                header(
+                    'Location: '.api_get_path(WEB_PATH)
+                    .'index.php?loginFailed=1&error=wrong_authentication_source'
+                );
                 exit;
             }
         } else {
@@ -220,7 +250,8 @@ class sso {
      * @param	string	Encoded cookie
      * @return  array   Parsed and unencoded cookie
      */
-    private function decode_cookie($cookie) {
+    private function decode_cookie($cookie)
+    {
         return unserialize(base64_decode($cookie));
     }
 }

+ 31 - 60
main/blog/blog.php

@@ -128,24 +128,21 @@ if (!empty($_GET['unregister']))
 	Blog :: set_user_unsubscribed((int)$_GET['blog_id'], (int)$_GET['user_id']);
 }
 
-if (isset($_GET['action']) && $_GET['action'] == 'manage_tasks')
-{
+if (isset($_GET['action']) && $_GET['action'] == 'manage_tasks') {
 	if (isset($_GET['do']) && $_GET['do'] == 'delete')
 	{
 		Blog :: delete_task($blog_id, (int)$_GET['task_id']);
 		$return_message = array('type' => 'confirmation', 'message' => get_lang('TaskDeleted'));
 	}
 
-	if (isset($_GET['do']) && $_GET['do'] == 'delete_assignment')
-	{
+	if (isset($_GET['do']) && $_GET['do'] == 'delete_assignment') {
 		Blog :: delete_assigned_task($blog_id, Database::escape_string((int)$_GET['task_id']), Database::escape_string((int)$_GET['user_id']));
 		$return_message = array('type' => 'confirmation', 'message' => get_lang('TaskAssignmentDeleted'));
 	}
 
 }
 
-if (isset($_GET['action']) && $_GET['action'] == 'view_post')
-{
+if (isset($_GET['action']) && $_GET['action'] == 'view_post') {
 	$task_id = (isset ($_GET['task_id']) && is_numeric($_GET['task_id'])) ? $_GET['task_id'] : 0;
 
 	if (isset($_GET['do']) && $_GET['do'] == 'delete_comment')
@@ -202,8 +199,7 @@ if (isset($_GET['action']) && $_GET['action'] == 'view_post')
 $htmlHeadXtra[] = '<script src="tbl_change.js" type="text/javascript"></script>';
 
 // Set bredcrumb
-switch ($current_page)
-{
+switch ($current_page) {
 	case 'new_post' :
 		$nameTools = get_lang('NewPost');
 		$interbreadcrumb[] = array ('url' => "blog.php?blog_id=$blog_id", "name" => Blog :: get_blog_title($blog_id));
@@ -240,14 +236,11 @@ switch ($current_page)
 }
 
 // feedback messages
-if (!empty($return_message))
-{
-	if ($return_message['type'] == 'confirmation')
-	{
+if (!empty($return_message)) {
+	if ($return_message['type'] == 'confirmation') {
 		Display::display_confirmation_message($return_message['message']);
 	}
-	if ($return_message['type'] == 'error')
-	{
+	if ($return_message['type'] == 'error') {
 		Display::display_error_message($return_message['message']);
 	}
 }
@@ -276,12 +269,12 @@ Display::display_introduction_section(TOOL_BLOG);
 	<td width="10%" style="float;left;" class="blog_left" valign="top">
 		<?php
 
-$month = (int)$_GET['month'] ? (int)$_GET['month'] : (int) date('m');
-$year = (int)$_GET['year'] ? (int)$_GET['year'] : date('Y');
-Blog :: display_minimonthcalendar($month, $year, $blog_id);
+$month = isset($_GET['month']) ? (int)$_GET['month'] : (int) date('m');
+$year = isset($_GET['year']) ? (int)$_GET['year'] : date('Y');
+
+Blog::display_minimonthcalendar($month, $year, $blog_id);
 ?>
 		<br />
-
 		<br />
 		<table width="100%">
 			<tr>
@@ -312,11 +305,11 @@ Blog :: display_minimonthcalendar($month, $year, $blog_id);
 	<td valign="top" class="blog_right">
 		<?php
 
-if ($error)
+if (isset($error)) {
 	Display :: display_error_message($message);
+}
 
-if ($flag == '1')
-{
+if (isset($flag) && $flag == '1') {
 	$current_page = "manage_tasks";
 	Blog :: display_assign_task_form($blog_id);
 }
@@ -358,21 +351,14 @@ switch ($current_page) {
 					Display::display_error_message(get_lang('FormHasErrorsPleaseComplete'));
 				}
 			Blog :: display_form_new_post($blog_id);
-		}
-		else
-		{
-				if (isset ($_GET['filter']) && !empty ($_GET['filter']))
-				{
+		} else {
+				if (isset($_GET['filter']) && !empty($_GET['filter'])) {
 					Blog :: display_day_results($blog_id, Database::escape_string($_GET['filter']));
-				}
-				else
-				{
+				} else {
 					Blog :: display_blog_posts($blog_id);
 				}
 			}
-		}
-		else
-		{
+		} else {
 			api_not_allowed();
 		}
 		break;
@@ -382,55 +368,43 @@ switch ($current_page) {
 	case 'edit_post' :
 		$task_id = (isset ($_GET['task_id']) && is_numeric($_GET['task_id'])) ? $_GET['task_id'] : 0;
 
-		if (api_is_allowed('BLOG_'.$blog_id, 'article_edit', $task_id))
-		{
+		if (api_is_allowed('BLOG_'.$blog_id, 'article_edit', $task_id)) {
 			// we show the form if
 			// 1. no post data
 			// 2. there is post data and the required field is empty
-			if (!$_POST OR (!empty($_POST) AND empty($_POST['post_title'])))
-			{
+			if (!$_POST OR (!empty($_POST) AND empty($_POST['post_title']))) {
 				// if there is post data there is certainly an error in the form
-				if ($_POST)
-				{
+				if ($_POST) {
 					Display::display_error_message(get_lang('FormHasErrorsPleaseComplete'));
 				}
                 Blog :: display_form_edit_post($blog_id, Database::escape_string((int)$_GET['post_id']));
-			}
-			else
-			{
-				if (isset ($_GET['filter']) && !empty ($_GET['filter']))
-				{
+			} else {
+				if (isset ($_GET['filter']) && !empty ($_GET['filter'])) {
 					Blog :: display_day_results($blog_id, Database::escape_string($_GET['filter']));
-				}
-				else
-				{
+				} else {
 					Blog :: display_blog_posts($blog_id);
 				}
 			}
-		}
-		else
-		{
+		} else {
 			api_not_allowed();
 		}
 
 		break;
 	case 'manage_members' :
-		if (api_is_allowed('BLOG_'.$blog_id, 'member_management'))
-		{
+		if (api_is_allowed('BLOG_'.$blog_id, 'member_management')) {
 			Blog :: display_form_user_subscribe($blog_id);
 			echo '<br /><br />';
 			Blog :: display_form_user_unsubscribe($blog_id);
-		}
-		else
+		} else {
 			api_not_allowed();
+        }
 
 		break;
 	case 'manage_rights' :
 		Blog :: display_form_user_rights($blog_id);
 		break;
 	case 'manage_tasks' :
-		if (api_is_allowed('BLOG_'.$blog_id, 'task_management'))
-		{
+		if (api_is_allowed('BLOG_'.$blog_id, 'task_management')) {
 			if (isset($_GET['do']) && $_GET['do'] == 'add')
 			{
 				Blog :: display_new_task_form($blog_id);
@@ -468,12 +442,9 @@ switch ($current_page) {
 		break;
 	case '' :
 	default :
-		if (isset ($_GET['filter']) && !empty ($_GET['filter']))
-		{
+		if (isset ($_GET['filter']) && !empty ($_GET['filter'])) {
 			Blog :: display_day_results($blog_id, Database::escape_string($_GET['filter']));
-		}
-		else
-		{
+		} else {
 			Blog :: display_blog_posts($blog_id);
 		}
 }

+ 3 - 3
main/blog/blog_admin.php

@@ -49,8 +49,8 @@ if (api_is_allowed_to_edit()) {
 		'name' => $current_section
 		);
 		Display::display_header('');
-	} else {		
-	}	
+	} else {
+	}
 	echo '<div class="actions">';
 	echo "<a href='".api_get_self()."?".api_get_cidreq()."&action=add'>",Display::return_icon('new_blog.png',get_lang('AddBlog'),'',ICON_SIZE_MEDIUM)."</a>";
 	echo '</div>';
@@ -83,7 +83,7 @@ if (api_is_allowed_to_edit()) {
 		Display::display_confirmation_message(get_lang('BlogDeleted'));
 	}
 
-	/*	
+	/*
 		DISPLAY
 	*/
 	//api_display_tool_title($nameTools);

+ 7 - 10
main/calendar/agenda.inc.php

@@ -795,9 +795,6 @@ function selectAll(cbList,bSelect,showwarning) {
 			}
 			document.new_calendar_item.submit();
 		}
-
-
-
 }
 
 function reverseAll(cbList)
@@ -847,15 +844,12 @@ function plus_ical() {
  */
 function user_group_filter_javascript()
 {
-    return "<script language=\"JavaScript\" type=\"text/JavaScript\">
-	<!--
+    return "<script>
 	function MM_jumpMenu(targ,selObj,restore){
 	  eval(targ+\".location='\"+selObj.options[selObj.selectedIndex].value+\"'\");
 	  if (restore) selObj.selectedIndex=0;
 	}
-	//-->
-	</script>
-	";
+	</script>";
 }
 
 /**
@@ -911,7 +905,6 @@ function get_course_users()
  */
 function get_course_groups()
 {
-    $group_list = array();
     $group_list = CourseManager::get_group_list_of_course(api_get_course_id(), api_get_session_id());
     return $group_list;
 }
@@ -1405,7 +1398,11 @@ function show_user_group_filter_form()
         $option = "<optgroup label=\"".get_lang("Groups")."\">";
         foreach ($group_list as $this_group) {
             // echo "<option value=\"agenda.php?isStudentView=true&amp;group=".$this_group['id']."\">".$this_group['name']."</option>";
-            $has_access = GroupManager::user_has_access(api_get_user_id(), $this_group['id'], GroupManager::GROUP_TOOL_CALENDAR);
+            $has_access = GroupManager::user_has_access(
+                api_get_user_id(),
+                $this_group['id'],
+                GroupManager::GROUP_TOOL_CALENDAR
+            );
             $result = GroupManager::get_group_properties($this_group['id']);
 
             if ($result['calendar_state'] != '0') {

+ 17 - 5
main/calendar/agenda.lib.php

@@ -1151,7 +1151,9 @@ class Agenda
         if (empty($courseInfo)) {
             return array();
         }
+
         $course_id = $courseInfo['real_id'];
+
         if (empty($course_id)) {
             return array();
         }
@@ -1183,6 +1185,7 @@ class Agenda
         $session_id = intval($session_id);
 
         if (is_array($group_memberships) && count($group_memberships) > 0) {
+
             if (api_is_allowed_to_edit()) {
                 if (!empty($groupId)) {
                     $where_condition = "( ip.to_group_id IN (0, ".implode(", ", $group_memberships).") ) ";
@@ -1197,8 +1200,15 @@ class Agenda
                 $where_condition = "( ip.to_user_id = $user_id OR ip.to_group_id IN (0, ".implode(", ", $group_memberships).") ) ";
             }
 
-            $sql = "SELECT DISTINCT agenda.*, ip.visibility, ip.to_group_id, ip.insert_user_id, ip.ref, to_user_id
-                    FROM $tlb_course_agenda agenda INNER JOIN $tbl_property ip
+            $sql = "SELECT DISTINCT
+                        agenda.*,
+                        ip.visibility,
+                        ip.to_group_id,
+                        ip.insert_user_id,
+                        ip.ref,
+                        to_user_id
+                    FROM $tlb_course_agenda agenda
+                    INNER JOIN $tbl_property ip
                     ON (agenda.id = ip.ref AND agenda.c_id = ip.c_id)
                     WHERE
                         ip.tool         ='".TOOL_CALENDAR_EVENT."' AND
@@ -1208,17 +1218,19 @@ class Agenda
                         ip.c_id         = $course_id
                     ";
         } else {
-            $visibilityCondition = "ip.visibility='1' AND";
+            $visibilityCondition = " ip.visibility='1' AND";
+
             if (api_is_allowed_to_edit()) {
                 if ($user_id == 0) {
                     $where_condition = "";
                 } else {
-                    $where_condition = "( ip.to_user_id=".$user_id. ") AND ";
+                    $where_condition = " ( ip.to_user_id = ".$user_id. " OR ip.to_group_id='0' ) AND ";
                 }
                 $visibilityCondition = " (ip.visibility IN ('1', '0')) AND ";
             } else {
-                $where_condition = "( ip.to_user_id=$user_id OR ip.to_group_id='0') AND ";
+                $where_condition = " ( ip.to_user_id = $user_id OR ip.to_group_id='0' ) AND ";
             }
+
             $sql = "SELECT DISTINCT agenda.*, ip.visibility, ip.to_group_id, ip.insert_user_id, ip.ref, to_user_id
                     FROM $tlb_course_agenda agenda INNER JOIN $tbl_property ip
                     ON (agenda.id = ip.ref AND agenda.c_id = ip.c_id)

+ 1 - 11
main/calendar/allagendas.php

@@ -158,17 +158,13 @@ function get_agenda_items_by_course_list($course_list, $month, $year, $session_i
 						ORDER BY start_date ";		
 		$result = Database::query($sqlquery);
 		while ($item = Database::fetch_array($result,'ASSOC')) {
-			//var_dump($item);
 			//taking the day
 			$agendaday = date("j",strtotime($item['start_date']));				
 			if(!isset($items[$agendaday])){$items[$agendaday]=array();}
 			//taking the time
 			$time = date("H:i",strtotime($item['start_date']));
-			//var_dump($time );
 			$end_time= date("H:i",strtotime($item['end_date']));
 			$URL = api_get_path(WEB_PATH)."main/calendar/allagendas.php?cidReq=".urlencode($code)."&amp;sort=asc&amp;view=list&amp;day=$agendaday&amp;month=$month&amp;year=$year#$agendaday"; // RH  //Patrick Cool: to highlight the relevant agenda item
-			//var_dump($URL);
-			
 			if ($setting_agenda_link == 'coursecode') {
 				//$title=$array_course_info['title'];
 				$agenda_link = api_substr($title, 0, 14);
@@ -260,14 +256,8 @@ if(!empty($my_session_id)) {
 	$_SESSION['my_course_list'] = array();	
 	$my_course_list = array();
 } else {
-	//echo 'here';
-	$my_course_list = $_SESSION['my_course_list'];
-	 
-	//var_dump($_SESSION['my_course_list'], $my_course_list);	
-	
+	$my_course_list = $_SESSION['my_course_list'];	
 	$my_course_list_keys = array_keys($my_course_list);
-	
-	//var_dump($my_course_list, $my_course_list_keys);
 	if (!in_array($my_course_id, $my_course_list_keys)) {	
 		$course_info = api_get_course_info_by_id($my_course_id);
 		$_SESSION['my_course_list'][$my_course_id] = $course_info;	

+ 4 - 6
main/calendar/ical_export.php

@@ -74,14 +74,12 @@ if (!empty($event)) {
             $vevent->setClass('PRIVATE');
             break;
     }
-    
+
     $event['start_date'] = api_get_local_time($event['start_date']);
     $event['end_date'] = api_get_local_time($event['end_date']);
-    
-    
-    
+
     switch($type) {
-        case 'personal':        
+        case 'personal':
         case 'platform':
             $vevent->setProperty( 'summary', api_convert_encoding($event['title'],'UTF-8', $charset));
             if(empty($event['start_date'])){header('location:'.Security::remove_XSS($_SERVER['HTTP_REFERER']));}
@@ -105,7 +103,7 @@ if (!empty($event)) {
             $ical->setComponent ($vevent); // add event to calendar
             $ical->returnCalendar();
             break;
-        case 'course':            
+        case 'course':
             $vevent->setProperty( 'summary', api_convert_encoding($event['title'],'UTF-8',$charset));
             if(empty($event['start_date'])){header('location:'.Security::remove_XSS($_SERVER['HTTP_REFERER']));}
             list($y,$m,$d,$h,$M,$s) = preg_split('/[\s:-]/',$event['start_date']);

+ 2 - 15
main/calendar/myagenda.inc.php

@@ -869,27 +869,15 @@ function show_simple_personal_agenda($user_id) {
 				$style = "datanow";
 				$text_style = "text";
 			}
-			/*--------------------------------------------------
-			 			display: date and time
-			  --------------------------------------------------*/
+
 			// adding an internal anchor
 			$content.= date("d", strtotime($myrow["date"]))." ".$MonthsLong[date("n", strtotime($myrow["date"])) - 1]." ".date("Y", strtotime($myrow["date"]))."&nbsp;";
 			$content.= strftime(get_lang("timeNoSecFormat"), strtotime($myrow["date"]));
 
-			/*--------------------------------------------------
-			 			display: the title
-			  --------------------------------------------------*/
 			$content.= '<br />';
 			$content.= $myrow['title'];
 			$content.= '<br />';
 
-			/*--------------------------------------------------
-			 			display: the content
-			  --------------------------------------------------*/
-			  /*
-			$content = $myrow['title'];
-			$content = make_clickable($content);
-			*/
 			return $content;
 		}
 	} else {
@@ -941,8 +929,7 @@ function get_personal_agenda_items_between_dates($user_id, $date_start='', $date
 	// get agenda-items for every course
 	$courses = api_get_user_courses($user_id,false);
     require_once(api_get_path(LIBRARY_PATH).'groupmanager.lib.php');
-	foreach ($courses as $id => $course)
-	{
+	foreach ($courses as $id => $course) {
 		$c = api_get_course_info($course['code']);
 		//databases of the courses
 		$t_a = Database :: get_course_table(TABLE_AGENDA, $course['db']);

+ 1 - 1
main/chat/chat_chat.php

@@ -33,7 +33,7 @@ if (!empty($course)) {
     list($pseudo_user) = Database::fetch_row($result);
 
     $isAllowed = !(empty($pseudo_user) || !$_cid);
-    $isMaster = (bool)$is_courseAdmin;
+    $isMaster = (bool)api_is_course_admin();
 
     $date_now = date('Y-m-d');
     $basepath_chat = '';

+ 1 - 1
main/chat/chat_hidden.php

@@ -30,7 +30,7 @@ $result = Database::query($query);
 list($pseudo_user) = Database::fetch_row($result);
 
 $isAllowed = !(empty($pseudo_user) || !$_cid);
-$isMaster = (bool)$is_courseAdmin;
+$isMaster = (bool)api_is_course_admin();
 
 $date_now = date('Y-m-d');
 

+ 11 - 18
main/chat/chat_message.php

@@ -79,7 +79,7 @@ if (!empty($course) && !empty($_user['user_id'])) {
     list($pseudo_user) = Database::fetch_row($result);
 
     $isAllowed = !(empty($pseudo_user) || !$_cid);
-    $isMaster = (bool)$is_courseAdmin;
+    $isMaster = (bool)api_is_course_admin();
 
     $user_id = Database::result($result, 0, 'user_id');
     $firstname = Database::result($result, 0, 'firstname');
@@ -215,21 +215,19 @@ if (!empty($course) && !empty($_user['user_id'])) {
 
 				$fp = fopen($chat_path.$basename_chat.'.log.html', 'a');
 					// view user picture
-					$user_image = UserManager::get_user_picture_path_by_id($user_id, 'web', false, true);
-					$user_photo = $user_image['dir'].'medium_'.$user_image['file'];
-					$file_photo = '<img class="chat-image" src="'.$user_photo.'"/>';
+					$userImage = UserManager::get_user_picture_path_by_id($user_id, 'web', false, true);
+                                        if (substr($userImage['file'],0,7) != 'unknown') {
+    					$userPhoto = $userImage['dir'].'medium_'.$userImage['file'];
+                                        } else {
+    					$userPhoto = $userImage['dir'].$userImage['file'];
+                                        }
+					$filePhoto = '<img class="chat-image" src="'.$userPhoto.'"/>';
 
 				if ($isMaster) {
-
-					// $photo = '<img src="'.api_get_path(WEB_IMG_PATH).'teachers.gif" alt="'.get_lang('Teacher').'"  width="11" height="11" align="top"  title="'.get_lang('Teacher').'"  />';
-					fputs($fp, '<div class="message-teacher"><div class="content-message"><div>'.$message.'</div><div class="message-date">'.$timeNow.'</div></div><div class="icon-message"></div>'.$file_photo.'</div>'."\n");
-					//fputs($fp, '<span style="color:#999; font-size: smaller;">['.$timeNow.']</span>'.$file_photo.' <span id="chat_login_name"><b>'.api_get_person_name($firstname, $lastname).'</b></span> : <i>'.$message.'</i><br />'."\n");
+					fputs($fp, '<div class="message-teacher"><div class="content-message"><div>'.$message.'</div><div class="message-date">'.$timeNow.'</div></div><div class="icon-message"></div>'.$filePhoto.'</div>'."\n");
 				} else {
-					// $photo = '<img src="'.api_get_path(WEB_IMG_PATH).'students.gif" alt="'.get_lang('Student').'"  width="11" height="11" align="top"  title="'.get_lang('Student').'"  />';
-					//fputs($fp, '<span style="color:#999; font-size: smaller;">['.$timeNow.']</span>'.$file_photo.' <b>'.api_get_person_name($firstname, $lastname).'</b> : <i>'.$message.'</i><br />'."\n");
-					fputs($fp, '<div class="message-student">'.$file_photo.'<div class="icon-message"></div><div class="content-message"><div>'.$message.'</div><div class="message-date">'.$timeNow.'</div></div></div>'."\n");
+					fputs($fp, '<div class="message-student">'.$filePhoto.'<div class="icon-message"></div><div class="content-message"><div>'.$message.'</div><div class="message-date">'.$timeNow.'</div></div></div>'."\n");
 				}
-
 				fclose($fp);
 
 				$chat_size = filesize($chat_path.$basename_chat.'.log.html');
@@ -250,7 +248,7 @@ if (!empty($course) && !empty($_user['user_id'])) {
         <textarea id="message" class="message-text" name="message" style=" <?php echo $talkboxsize; ?>" onkeydown="send_message(event);" onclick="javascript: insert_smile(this);"></textarea>
         </td>
         <td>
-            <button id="send" type="submit" value="<?php echo get_lang('Send'); ?>" class="btn-enviar"><?php echo get_lang('Send'); ?></button>
+        <button id="send" type="submit" value="<?php echo get_lang('Send'); ?>" class="btn-send"><?php echo get_lang('Send'); ?></button>
         </td>
 	</tr>
 	</table>
@@ -287,11 +285,6 @@ if (!empty($course) && !empty($_user['user_id'])) {
 		?>
     </div>
     </form>
-    <!-- <audio id="audio">
-	    <source type="audio/wav" src="sound/sonido_notificacion.wav"></source>
-	    <source type="audio/ogg" src="sound/sonido_notificacion.ogg"></source>
-	    <source type="audio/mpeg" src="sound/sonido_notificacion.mp3"></source>
-    </audio> -->
 <?php
 }
 require 'footer_frame.inc.php';

+ 21 - 13
main/chat/chat_whoisonline.php

@@ -43,8 +43,8 @@ if (!empty($course)) {
 
     list($pseudo_user) = Database::fetch_array($result);
 
-    $isAllowed = !(empty($pseudo_user) || !$_cid);
-    $isMaster = (bool)$is_courseAdmin;
+	$isAllowed = !(empty($pseudo_user) || !$_cid);
+	$isMaster = api_is_course_admin();
 
     $date_inter = date('Y-m-d H:i:s', time() - 120);
 
@@ -109,11 +109,10 @@ if (!empty($course)) {
 	?>
 	<div class="user-connected">
 	<div id="user-online-scroll" class="user-online">
-
 		<div class="title"><?php echo get_lang('Users'); ?> <?php echo get_lang('Connected'); ?></div>
 		<div class="scrollbar"><div class="track"><div class="thumb"><div class="end"></div></div></div></div>
 		<div class="viewport"><div id="hidden" class="overview">
-		<ul class="perfil list-group">
+		<ul class="profile list-group">
 			<?php
 				foreach ($users as & $user) {
 					if (empty($session_id)) {
@@ -121,23 +120,32 @@ if (!empty($course)) {
 					} else {
 						$status = CourseManager::is_course_teacher($user['user_id'], $_SESSION['_course']['id']) ? 1 : 5;
 					}
-					$user_image = UserManager::get_user_picture_path_by_id($user['user_id'], 'web', false, true);
-					$file_url = $user_image['dir'].'medium_'.$user_image['file'];
-					$email_url = $user['email'];
-					$url_user_profile=api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user['user_id'].'&';
+				$userImage = UserManager::get_user_picture_path_by_id($user['user_id'], 'web', false, true);
+                                if (substr($userImage['file'],0,7) != 'unknown') {
+				    $fileUrl = $userImage['dir'].'medium_'.$userImage['file'];
+                                } else {
+				    $fileUrl = $userImage['dir'].$userImage['file'];
+                                }
+				$email = $user['email'];
+				$url_user_profile=api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user['user_id'].'&';
 			?>
 			<li class="list-group-item">
-				<img src="<?php echo $file_url;?>" border="0" width="50" alt="" class="user-image-chat" />
+				<img src="<?php echo $fileUrl;?>" border="0" width="50" alt="" class="user-image-chat" />
 				<div class="user-name">
 					<a href="<?php echo $url_user_profile; ?>" target="_blank"><?php echo api_get_person_name($user['firstname'], $user['lastname']); ?></a>
-					<?php if ($status == 1) echo Display::return_icon('teachers.gif', get_lang('Teacher'), array('height' => '18')).' '; else echo Display::return_icon('students.gif', get_lang('Student'), array('height' => '18')); ?>
+					<?php
+						if ($status == 1) {
+							echo Display::return_icon('teachers.gif', get_lang('Teacher'), array('height' => '18'));
+						}else{
+							echo Display::return_icon('students.gif', get_lang('Student'), array('height' => '18'));
+						}
+					?>
 				</div>
-				<div class="user-email"><?php echo $email_url; ?></div>
-				
+				<div class="user-email"><?php echo $email; ?></div>
 			</li>
 			<?php  } unset($users); ?>
 		</ul>
 	</div></div></div></div>
 	<?php
 }
-require 'footer_frame.inc.php';
+require 'footer_frame.inc.php';

+ 3 - 3
main/chat/footer_frame.inc.php

@@ -9,9 +9,9 @@
  */
 ?>
 <audio id="audio">
-	<source src="sound/sonido_notificacion.wav" type="audio/wav"></source>
-	<source src="sound/sonido_notificacion.ogg" type="audio/ogg"></source>
-	<source src="sound/sonido_notificacion.mp3" type="audio/mpeg"></source>
+	<source src="<?php echo api_get_path(WEB_PATH);?>main/chat/sound/notification.wav" type="audio/wav"></source>
+	<source src="<?php echo api_get_path(WEB_PATH);?>main/chat/sound/notification.ogg" type="audio/ogg"></source>
+	<source src="<?php echo api_get_path(WEB_PATH);?>main/chat/sound/notification.mp3" type="audio/mpeg"></source>
 </audio>
 </body>
 </html>

+ 41 - 79
main/chat/header_frame.inc.php

@@ -18,7 +18,7 @@ $bodyXtra = ' class="course_chat" dir="'.api_get_text_direction().'" ';
 
 if (FRAME == 'hidden') {
 	$bodyXtra .= 'onload="javascript: updateChat(); updateConnected(); setTimeout(\'submitHiddenForm();\', 5000);"';
-    // Change timeout to change refresh time of the chat window
+	// Change timeout to change refresh time of the chat window
 } elseif (FRAME == 'message') {
 	$bodyXtra .= 'onload="javascript: eventMessage();"';
 }
@@ -45,91 +45,53 @@ header('Content-Type: text/html; charset='.api_get_system_encoding());
 ?><!DOCTYPE html>
 <html>
 <head>
-    <meta charset="<?php echo api_get_system_encoding(); ?>" />
-    <title>Chat</title>
-    <link rel="stylesheet" type="text/css" href="<?php echo api_get_path(WEB_CSS_PATH); ?>chat.css">
-    <?php echo api_get_js('jquery.min.js'); ?>
-    <?php echo api_get_js('jquery.tinyscrollbar.js'); ?>
-    <script type="text/javascript">
-        /*$(document).ready(function() {
-        var posicion = $("#clear-chat").offset();
-        var margenSuperior = 15;
-         $(window).scroll(function() {
-             if ($(window).scrollTop() > posicion.top) {
-                 $("#clear-chat").stop().animate({
-                     marginTop: $(window).scrollTop() - posicion.top + margenSuperior
-                 });
-             } else {
-                 $("#clear-chat").stop().animate({
-                     marginTop: 0
-                 });
-             };
-         });
-        });*/
-        $(document).ready(function(){
-                $('#user-online-scroll').tinyscrollbar();
-            });
-        //Sound send message
-        /*$(document).ready(function(){
-            $("#message").keypress(function(event){
-            if (event.which == 13) {
-                $("#audio")[0].play();
-                }
-            });
-        });
-        $(document).ready(function(){
-            $("#send").on("click",function(){
-                $("#audio")[0].play();
-            });
-        });*/
-        
-    </script>
-
+<meta charset="<?php echo api_get_system_encoding(); ?>" />
+<title>Chat</title>
+<link rel="stylesheet" type="text/css" href="<?php echo api_get_path(WEB_CSS_PATH); ?>chat.css">
+<?php echo api_get_js('jquery.min.js'); ?>
+<?php echo api_get_js('jquery.tinyscrollbar.js'); ?>
 <script type="text/javascript">
-function play_notification() {
-    document.getElementById('audio').play();
-}
-function updateChat()
-{
-	if ('<?php echo $chat_size_old; ?>' != '<?php echo $chat_size_new; ?>') {
-		parent.chat_chat.location.href='chat_chat.php?size=<?php echo $chat_size_new.'&'.api_get_cidreq(); ?>#bottom';
-        play_notification();
-	}
-}
+	$(document).ready(function(){
+		$('#user-online-scroll').tinyscrollbar();
+	});
 
-function updateConnected()
-{
-
-	if ('<?php echo $connected_old; ?>' != '<?php echo $connected_new; ?>')
-	{
-		parent.chat_whoisonline.location.href='chat_whoisonline.php?size=<?php echo $connected_new; ?>';
+	function play_notification() {
+		document.getElementById('audio').play();
 	}
-}
 
-function submitHiddenForm()
-{
-	document.formHidden.submit();
-}
+	function updateChat(){
+		if ('<?php echo $chat_size_old; ?>' != '<?php echo $chat_size_new; ?>') {
+			parent.chat_chat.location.href='chat_chat.php?size=<?php echo $chat_size_new.'&'.api_get_cidreq(); ?>#bottom';
+			play_notification();
+		}
+	}
 
-function eventMessage()
-{
-	<?php if (isset($chat_size) && $chat_size): ?>
-	parent.chat_hidden.document.formHidden.chat_size_old.value='<?php echo $chat_size; ?>';
-	parent.chat_chat.location.href='chat_chat.php?size=<?php echo $chat_size.'&'.api_get_cidreq(); ?>#bottom';
-	<?php endif; ?>
-	document.formMessage.message.focus();
+	function updateConnected(){
+		if ('<?php echo $connected_old; ?>' != '<?php echo $connected_new; ?>'){
+			parent.chat_whoisonline.location.href='chat_whoisonline.php?size=<?php echo $connected_new; ?>';
+		}
+	}
 
-}
+	function submitHiddenForm(){
+		document.formHidden.submit();
+	}
 
-function send_message(evenement) {
-    for (prop in evenement) {
-    	if(prop == 'which') touche = evenement.which; else touche = evenement.keyCode;
-    }
+	function eventMessage(){
+		<?php if (isset($chat_size) && $chat_size): ?>
+		parent.chat_hidden.document.formHidden.chat_size_old.value='<?php echo $chat_size; ?>';
+		parent.chat_chat.location.href='chat_chat.php?size=<?php echo $chat_size.'&'.api_get_cidreq(); ?>#bottom';
+		<?php endif; ?>
+		document.formMessage.message.focus();
+	}
 
-    if (touche == 13) {
-    	document.formMessage.submit();
-    }
-}
+	function send_message(keyEvent) {
+		for (prop in keyEvent) {
+			if(prop == 'which') key = keyEvent.which; else key = keyEvent.keyCode;
+		}
+		if (key == 13) {
+			document.formMessage.submit();
+		}
+	}
 </script>
 </head>
-<body <?php echo $bodyXtra; ?> >
+<body <?php echo $bodyXtra; ?> >

BIN
main/chat/sound/notification.mp3


BIN
main/chat/sound/notification.ogg


BIN
main/chat/sound/notification.wav


BIN
main/chat/sound/sonido_notificacion.mp3


BIN
main/chat/sound/sonido_notificacion.ogg


BIN
main/chat/sound/sonido_notificacion.wav


+ 1 - 0
main/course_description/add.php

@@ -9,6 +9,7 @@
 
 // protect a course script
 api_protect_course_script(true);
+
 // display categories
 $categories = array ();
 foreach ($default_description_titles as $id => $title) {

+ 7 - 2
main/course_description/course_description_controller.php

@@ -41,8 +41,13 @@ class CourseDescriptionController
         $data['messages'] = $messages;
         $browser = api_get_navigator();
 
-        if (strpos($data['descriptions'], '<iframe') !== false && $browser['name'] == 'Chrome') {
-            header("X-XSS-Protection: 0");
+        if (!is_array($data['descriptions'])) {
+            $data['descriptions'] = array($data['descriptions']);
+        }
+        foreach ($data['descriptions'] as $description) {
+            if (strpos($description, '<iframe') !== false && $browser['name'] == 'Chrome') {
+                header("X-XSS-Protection: 0");
+            }
         }
 
         // render to the view

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

@@ -113,7 +113,7 @@ elseif ($restore) { // visibility 0,2 -> 1
 elseif (isset($update) && $update) {
 	$result 	= Database::query("SELECT * FROM $TBL_ACCUEIL WHERE c_id = $course_id AND id=$id");
 	$tool		= Database::fetch_array($result);
-	$racine		= $_configuration['root_sys'].'/'.$currentCourseID.'/images/';
+	$racine		= api_get_path(SYS_PATH).'/'.$currentCourseID.'/images/';
 	$chemin		= $racine;
 	$name		= $tool[1];
 	$image		= $tool[3];
@@ -165,7 +165,7 @@ elseif (isset($update) && $update) {
 
 // Work with data post askable by admin of  course
 
-if ($is_platformAdmin && api_is_allowed_to_edit(null, true) && !api_is_coach()) {
+if (api_is_platform_admin() && api_is_allowed_to_edit(null, true) && !api_is_coach()) {
 	// Show message to confirm that a tools must be hide  from aivailable tools
 	// visibility 0,1->2
 	if ($askDelete) {
@@ -219,7 +219,7 @@ if (api_is_allowed_to_edit(null, true) && !api_is_coach()) {
 
 /*	TOOLS FOR PLATFORM ADMIN ONLY */
 
-if ($is_platformAdmin && api_is_allowed_to_edit(null, true) && !api_is_coach()) {
+if (api_is_platform_admin() && api_is_allowed_to_edit(null, true) && !api_is_coach()) {
 	$content .=  "<tr>"."<td colspan=\"6\">".
 		"<hr noshade size=\"1\" />".
 		"</td>"."</tr>\n".

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