Pārlūkot izejas kodu

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

aragonc 9 gadi atpakaļ
vecāks
revīzija
f170de7b97
42 mainītis faili ar 1869 papildinājumiem un 936 dzēšanām
  1. 14 25
      main/admin/course_edit.php
  2. 2 1
      main/admin/group_list.php
  3. 0 38
      main/admin/user_edit.php
  4. 16 19
      main/course_info/infocours.php
  5. 154 47
      main/create_course/add_course.php
  6. 18 35
      main/document/edit_document.php
  7. 59 36
      main/dropbox/dropbox_class.inc.php
  8. 16 16
      main/dropbox/dropbox_config.inc.php
  9. 78 26
      main/dropbox/dropbox_functions.inc.php
  10. 15 13
      main/dropbox/index.php
  11. 12 28
      main/exercice/answer.class.php
  12. 119 28
      main/exercice/exercise.class.php
  13. 0 10
      main/exercice/exercise_result.php
  14. 876 106
      main/exercice/fill_blanks.class.php
  15. 20 24
      main/exercice/question.class.php
  16. 2 1
      main/exercice/stats.php
  17. 1 2
      main/forum/forumfunction.inc.php
  18. 0 17
      main/inc/ajax/course_home.ajax.php
  19. 0 4
      main/inc/lib/SequenceManager.php
  20. 2 34
      main/inc/lib/add_course.lib.inc.php
  21. 3 3
      main/inc/lib/api.lib.php
  22. 76 150
      main/inc/lib/exercise.lib.php
  23. 20 17
      main/inc/lib/exercise_show_functions.lib.php
  24. 4 4
      main/inc/lib/export.lib.inc.php
  25. 61 0
      main/inc/lib/extra_field.lib.php
  26. 1 1
      main/inc/lib/formvalidator/Element/Number.php
  27. 33 18
      main/inc/lib/notebook.lib.php
  28. 1 1
      main/inc/lib/sessionmanager.lib.php
  29. 78 73
      main/lang/english/trad4all.inc.php
  30. 29 2
      main/lang/french/trad4all.inc.php
  31. 58 58
      main/lang/spanish/trad4all.inc.php
  32. 0 16
      main/newscorm/learnpath.class.php
  33. 41 39
      main/session/session_course_user.php
  34. 11 13
      main/template/default/mail/content_registration_platform.tpl
  35. 9 12
      main/template/default/mail/content_subscription_to_session_confirmation.tpl
  36. 1 1
      main/template/default/mail/cron_course_finished_body.tpl
  37. 1 1
      main/template/default/mail/cron_remind_course_expiration_body.tpl
  38. 8 6
      main/user/subscribe_user.php
  39. 3 7
      main/user/user.php
  40. 21 0
      tests/datafiller/data_courses.php
  41. 4 2
      tests/main/chat/chat_functions.lib.test.php
  42. 2 2
      tests/scripts/insert_session_fields.php

+ 14 - 25
main/admin/course_edit.php

@@ -323,20 +323,22 @@ if ($form->validate()) {
         $department_url = 'http://' . $department_url;
     }
 
-    $sql = "UPDATE $course_table SET
-                course_language='" . Database::escape_string($course_language) . "',
-                title='" . Database::escape_string($title) . "',
-                category_code='" . Database::escape_string($category_code) . "',
-                visual_code='" . Database::escape_string($visual_code) . "',
-                department_name='" . Database::escape_string($department_name) . "',
-                department_url='" . Database::escape_string($department_url) . "',
-                disk_quota='" . Database::escape_string($disk_quota) . "',
-                visibility = '" . Database::escape_string($visibility) . "',
-                subscribe = '" . Database::escape_string($subscribe) . "',
-                unsubscribe='" . Database::escape_string($unsubscribe) . "'
-            WHERE id = $courseId ";
     Database::query($sql);
 
+    $params = [
+        'course_language' => $course_language,
+        'title' => $title,
+        'category_code' => $category_code,
+        'visual_code' => $visual_code,
+        'department_name' => $department_name,
+        'department_url' => $department_url,
+        'disk_quota' => $disk_quota,
+        'visibility' => $visibility,
+        'subscribe' => $subscribe,
+        'unsubscribe' => $unsubscribe,
+    ];
+    Database::update($course_table, $params, ['id = ?' => $courseId]);
+
     // update the extra fields
     $courseFieldValue = new ExtraFieldValue('course');
     $courseFieldValue->saveFieldValues($course);
@@ -377,17 +379,6 @@ if ($form->validate()) {
         }
     }
 
-    // No need to register me as a teacher.
-    /*
-    $sql = "INSERT IGNORE INTO " . $course_user_table . " SET
-            c_id = " . $courseInfo['real_id'] . ",
-            user_id = '" . $tutor_id . "',
-            status = '1',
-            is_tutor ='0',
-            sort = '0',
-            user_course_cat='0'";
-    Database::query($sql);
-    */
     if (array_key_exists('add_teachers_to_sessions_courses', $courseInfo)) {
         $sql = "UPDATE $course_table SET
                 add_teachers_to_sessions_courses = '$addTeacherToSessionCourses'
@@ -396,8 +387,6 @@ if ($form->validate()) {
     }
 
     $course_id = $courseInfo['real_id'];
-    /* $forum_config_table = Database::get_course_table(TOOL_FORUM_CONFIG_TABLE);
-      $sql = "UPDATE ".$forum_config_table." SET default_lang='".Database::escape_string($course_language)."' WHERE c_id = $course_id "; */
 
     Display::addFlash(Display::return_message(get_lang('ItemUpdated')));
 

+ 2 - 1
main/admin/group_list.php

@@ -249,7 +249,8 @@ function lock_unlock_user($status, $user_id) {
     }
 
     if (($status_db == '1' OR $status_db == '0') AND is_numeric($user_id)) {
-        $sql = "UPDATE $user_table SET active=".intval($status_db)." WHERE user_id=".intval($user_id)."";
+        $sql = "UPDATE $user_table SET active=".intval($status_db)."
+                WHERE user_id=".intval($user_id)."";
         $result = Database::query($sql);
     }
 

+ 0 - 38
main/admin/user_edit.php

@@ -396,44 +396,6 @@ if ($form->validate()) {
         $extraFieldValue = new ExtraFieldValue('user');
         $extraFieldValue->saveFieldValues($user);
 
-		/*foreach ($user as $key => $value) {
-			if (substr($key, 0, 6) == 'extra_') {
-                //an extra field
-                //@todo remove this as well as in the profile.php ad put it in a function
-                if (is_array($value) && isset($value['Y']) && isset($value['F']) && isset($value['d'])) {
-                    if (isset($value['H']) && isset($value['i'])) {
-                        // extra field date time
-                        $time = mktime($value['H'],$value['i'],0,$value['F'],$value['d'],$value['Y']);
-                        $value = date('Y-m-d H:i:s',$time);
-                    } else {
-                        // extra field date
-                        $time = mktime(0,0,0,$value['F'],$value['d'],$value['Y']);
-                        $value = date('Y-m-d',$time);
-                    }
-                }
-                // For array $value -> if exists key 'tmp_name' then must not be empty
-                // This avoid delete from user field value table when doesn't upload a file
-                if (is_array($value)) {
-                    if (array_key_exists('tmp_name', $value) && empty($value['tmp_name'])) {
-                        //Nothing to do
-                    } else {
-                        if (array_key_exists('tmp_name', $value)) {
-                            $value['tmp_name'] = Security::filter_filename($value['tmp_name']);
-                        }
-                        if (array_key_exists('name', $value)) {
-                            $value['name'] = Security::filter_filename($value['name']);
-                        }
-                        UserManager::update_extra_field_value($user_id, substr($key, 6), $value);
-                    }
-                } else {
-                    UserManager::update_extra_field_value($user_id, 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);
-            }
-		}*/
 		$tok = Security::get_token();
 		header('Location: user_list.php?action=show_message&message='.urlencode(get_lang('UserUpdated')).'&sec_token='.$tok);
 		exit();

+ 16 - 19
main/course_info/infocours.php

@@ -479,27 +479,24 @@ if ($form->validate() && is_settings_editable()) {
         'activate_legal'
     );
 
-    foreach ($updateValues as $index =>$value) {
-        $updateValues[$index] = Database::escape_string($value);
-    }
-
     $activeLegal = isset($updateValues['activate_legal']) ? $updateValues['activate_legal'] : '';
-
     $table_course = Database :: get_main_table(TABLE_MAIN_COURSE);
-    $sql = "UPDATE $table_course SET
-        title 				    = '".$updateValues['title']."',
-        course_language 	    = '".$updateValues['course_language']."',
-        category_code 		    = '".$updateValues['category_code']."',
-        department_name  	    = '".$updateValues['department_name']."',
-        department_url  	    = '".$updateValues['department_url']."',
-        visibility  		    = '".$updateValues['visibility']."',
-        subscribe  			    = '".$updateValues['subscribe']."',
-        unsubscribe  		    = '".$updateValues['unsubscribe']."',
-        legal                   = '".$updateValues['legal']."',
-        activate_legal          = '".$activeLegal."',
-        registration_code 	    = '".$updateValues['course_registration_password']."'
-        WHERE id = $courseId";
-    Database::query($sql);
+
+    $params = [
+        'title' => $updateValues['title'],
+        'course_language' => $updateValues['course_language'],
+        'category_code' => $updateValues['category_code'],
+        'department_name' => $updateValues['department_name'],
+        'department_url' => $updateValues['department_url'],
+        'visibility' => $updateValues['visibility'],
+        'subscribe' => $updateValues['subscribe'],
+        'unsubscribe' => $updateValues['unsubscribe'],
+        'legal' => $updateValues['legal'],
+        'activate_legal' => $activeLegal,
+        'registration_code' => $updateValues['course_registration_password'],
+    ];
+
+    Database::update($table_course, $params, ['id = ?' => $courseId]);
 
     // Insert/Updates course_settings table
     foreach ($courseSettings as $setting) {

+ 154 - 47
main/create_course/add_course.php

@@ -39,14 +39,20 @@ $htmlHeadXtra[] = '<script type="text/javascript">
     });
 </script>';
 
-$interbreadcrumb[] = array('url' => api_get_path(WEB_PATH).'user_portal.php', 'name' => get_lang('MyCourses'));
+$interbreadcrumb[] = array(
+    'url' => api_get_path(WEB_PATH) . 'user_portal.php',
+    'name' => get_lang('MyCourses')
+);
 
 // Displaying the header.
 $tool_name = $course_validation_feature ? get_lang('CreateCourseRequest') : get_lang('CreateSite');
 
 $tpl = new Template($tool_name);
 
-if (api_get_setting('allow_users_to_create_courses') == 'false' && !api_is_platform_admin()) {
+if (
+    api_get_setting('allow_users_to_create_courses') == 'false' &&
+    !api_is_platform_admin()
+) {
     api_not_allowed(true);
 }
 
@@ -63,75 +69,152 @@ $form = new FormValidator('add_course');
 $form->addElement('header', $tool_name);
 
 // Title
-$form->addElement('text', 'title', array(get_lang('CourseName'), get_lang('Ex')), array('class' => 'span6', 'id' => 'title'));
+$form->addElement(
+    'text',
+    'title',
+    array(
+        get_lang('CourseName'),
+        get_lang('Ex')
+    ),
+    array(
+        'class' => 'span6',
+        'id' => 'title'
+    )
+);
 $form->applyFilter('title', 'html_filter');
 $form->addRule('title', get_lang('ThisFieldIsRequired'), 'required');
 
 $form->addButtonAdvancedSettings('advanced_params');
-$form->addElement('html','<div id="advanced_params_options" style="display:none">');
+$form->addElement(
+    'html',
+    '<div id="advanced_params_options" style="display:none">'
+);
 
 // Category category.
-$url = api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=search_category';
+$url = api_get_path(WEB_AJAX_PATH) . 'course.ajax.php?a=search_category';
 
 $form->addElement(
     'select_ajax',
     'category_code',
     get_lang('CourseFaculty'),
     null,
-    array(
-        'url' => $url
-    )
+    array('url' => $url)
 );
 
 // Course code
-$form->addText('wanted_code', array(get_lang('Code'), get_lang('OnlyLettersAndNumbers')), '', array('maxlength' => CourseManager::MAX_COURSE_LENGTH_CODE));
+$form->addText(
+    'wanted_code',
+    array(
+        get_lang('Code'),
+        get_lang('OnlyLettersAndNumbers')
+    ),
+    '',
+    array(
+        'maxlength' => CourseManager::MAX_COURSE_LENGTH_CODE,
+        'pattern' => '[a-zA-Z0-9]+',
+        'title' => get_lang('OnlyLettersAndNumbers')
+    )
+);
 $form->applyFilter('wanted_code', 'html_filter');
-$form->addRule('wanted_code', get_lang('Max'), 'maxlength', CourseManager::MAX_COURSE_LENGTH_CODE);
+$form->addRule(
+    'wanted_code',
+    get_lang('Max'),
+    'maxlength',
+    CourseManager::MAX_COURSE_LENGTH_CODE
+);
 
 // The teacher
-$titular = & $form->addElement('hidden', 'tutor_name', ''); //array(get_lang('Professor'), null), null, array('size' => '60', 'disabled' => 'disabled'));
+//array(get_lang('Professor'), null), null, array('size' => '60', 'disabled' => 'disabled'));
+$titular = & $form->addElement('hidden', 'tutor_name', '');
 if ($course_validation_feature) {
 
     // Description of the requested course.
-    $form->addElement('textarea', 'description', get_lang('Description'), array('rows' => '3'));
+    $form->addElement(
+        'textarea',
+        'description',
+        get_lang('Description'),
+        array('rows' => '3')
+    );
 
     // Objectives of the requested course.
-    $form->addElement('textarea', 'objetives', get_lang('Objectives'), array('rows' => '3'));
+    $form->addElement(
+        'textarea',
+        'objetives',
+        get_lang('Objectives'),
+        array('rows' => '3')
+    );
 
     // Target audience of the requested course.
-    $form->addElement('textarea', 'target_audience', get_lang('TargetAudience'), array('rows' => '3'));
+    $form->addElement(
+        'textarea',
+        'target_audience',
+        get_lang('TargetAudience'),
+        array('rows' => '3')
+    );
 }
 
 // Course language.
-$form->addElement('select_language', 'course_language', get_lang('Ln'), array(), array('style'=>'width:150px'));
+$form->addElement(
+    'select_language',
+    'course_language',
+    get_lang('Ln'),
+    array(),
+    array('style' => 'width:150px')
+);
 $form->applyFilter('select_language', 'html_filter');
 
 // Exemplary content checkbox.
-$form->addElement('checkbox', 'exemplary_content', null, get_lang('FillWithExemplaryContent'));
+$form->addElement(
+    'checkbox',
+    'exemplary_content',
+    null,
+    get_lang('FillWithExemplaryContent')
+);
 
 if ($course_validation_feature) {
 
-    // A special URL to terms and conditions that is set in the platform settings page.
-    $terms_and_conditions_url = trim(api_get_setting('course_validation_terms_and_conditions_url'));
+    // A special URL to terms and conditions that is set
+    // in the platform settings page.
+    $terms_and_conditions_url = trim(
+        api_get_setting('course_validation_terms_and_conditions_url')
+    );
 
-    // If the special setting is empty, then we may get the URL from Chamilo's module "Terms and conditions", if it is activated.
+    // If the special setting is empty,
+    // then we may get the URL from Chamilo's module "Terms and conditions",
+    // if it is activated.
     if (empty($terms_and_conditions_url)) {
         if (api_get_setting('allow_terms_conditions') == 'true') {
-            $terms_and_conditions_url = api_get_path(WEB_CODE_PATH).'auth/inscription.php?legal';
+            $terms_and_conditions_url = api_get_path(WEB_CODE_PATH);
+            $terms_and_conditions_url .= 'auth/inscription.php?legal';
         }
     }
 
     if (!empty($terms_and_conditions_url)) {
         // Terms and conditions to be accepted before sending a course request.
-        $form->addElement('checkbox', 'legal', null, get_lang('IAcceptTermsAndConditions'), 1);
-        $form->addRule('legal', get_lang('YouHaveToAcceptTermsAndConditions'), 'required');
+        $form->addElement(
+            'checkbox',
+            'legal',
+            null,
+            get_lang('IAcceptTermsAndConditions'),
+            1
+        );
+        $form->addRule(
+            'legal', get_lang('YouHaveToAcceptTermsAndConditions'),
+            'required'
+        );
         // Link to terms and conditions.
-        $link_terms_and_conditions = '<script>
-        function MM_openBrWindow(theURL,winName,features) { //v2.0
-            window.open(theURL,winName,features);
-        }
-        </script><a href="#" onclick="javascript: MM_openBrWindow(\''.$terms_and_conditions_url.'\',\'Conditions\',\'scrollbars=yes, width=800\')">';
-        $link_terms_and_conditions .= get_lang('ReadTermsAndConditions').'</a>';
+        $link_terms_and_conditions = '
+            <script>
+            function MM_openBrWindow(theURL, winName, features) { //v2.0
+                window.open(theURL,winName,features);
+            }
+            </script>
+        ';
+        $link_terms_and_conditions .= Display::url(
+            get_lang('ReadTermsAndConditions'),
+            '#',
+            ['onclick' => "javascript:MM_openBrWindow('$terms_and_conditions_url', 'Conditions', 'scrollbars=yes, width=800');"]
+        );
         $form->addElement('label', null, $link_terms_and_conditions);
     }
 }
@@ -139,7 +222,7 @@ if ($course_validation_feature) {
 $obj = new GradeModel();
 $obj->fill_grade_model_select_in_form($form);
 
-$form->addElement('html','</div>');
+$form->addElement('html', '</div>');
 
 // Submit button.
 $form->addButtonCreate($course_validation_feature ? get_lang('CreateThisCourseRequest') : get_lang('CreateCourseArea'));
@@ -189,12 +272,12 @@ if ($form->validate()) {
         if (!$course_validation_feature) {
 
             $params = array();
-            $params['title']                = $title;
-            $params['exemplary_content']    = $exemplary_content;
-            $params['wanted_code']          = $wanted_code;
-            $params['course_category']        = $category_code;
-            $params['course_language']      = $course_language;
-            $params['gradebook_model_id']   = isset($course_values['gradebook_model_id']) ? $course_values['gradebook_model_id'] : null;
+            $params['title'] = $title;
+            $params['exemplary_content'] = $exemplary_content;
+            $params['wanted_code'] = $wanted_code;
+            $params['course_category'] = $category_code;
+            $params['course_language'] = $course_language;
+            $params['gradebook_model_id'] = isset($course_values['gradebook_model_id']) ? $course_values['gradebook_model_id'] : null;
 
             $course_info = CourseManager::create_course($params);
 
@@ -213,12 +296,18 @@ if ($form->validate()) {
                 $add_course_tpl = $tpl->get_template('create_course/add_course.tpl');
                 $message = $tpl->fetch($add_course_tpl);*/
 
-                $url = api_get_path(WEB_CODE_PATH).'course_info/start.php?cidReq='.$course_info['code'].'&first=1';
-                header('Location: '.$url);
+                $url = api_get_path(WEB_CODE_PATH);
+                $url .= 'course_info/start.php?cidReq=';
+                $url .= $course_info['code'];
+                $url .= '&first=1';
+                header('Location: ' . $url);
                 exit;
-
             } else {
-                $message = Display :: return_message(get_lang('CourseCreationFailed'), 'error', false);
+                $message = Display::return_message(
+                    get_lang('CourseCreationFailed'),
+                    'error',
+                    false
+                );
                 // Display the form.
                 $content = $form->returnForm();
             }
@@ -238,25 +327,43 @@ if ($form->validate()) {
 
             if ($request_id) {
                 $course_request_info = CourseRequestManager::get_course_request_info($request_id);
-                $message = (is_array($course_request_info) ? '<strong>'.$course_request_info['code'].'</strong> : ' : '').get_lang('CourseRequestCreated');
-                $message = Display :: return_message($message, 'confirmation', false);
-                $message .=  '<div style="float: left; margin:0px; padding: 0px;">' .
-                    '<a class="btn btn-default" href="'.api_get_path(WEB_PATH).'user_portal.php">'.get_lang('Enter').'</a>' .
-                    '</div>';
+                $message = (is_array($course_request_info) ? '<strong>' . $course_request_info['code'] . '</strong> : ' : '') . get_lang('CourseRequestCreated');
+                $message = Display::return_message(
+                    $message,
+                    'confirmation',
+                    false
+                );
+                $message .= Display::tag(
+                    'div',
+                    Display::url(
+                        get_lang('Enter'),
+                        api_get_path(WEB_PATH) . 'user_portal.php',
+                        ['class' => 'btn btn-default']
+                    ),
+                    ['style' => 'float: left; margin:0px; padding: 0px;']
+                );
             } else {
-                $message = Display :: return_message(get_lang('CourseRequestCreationFailed'), 'error', false);
+                $message = Display::return_message(
+                    get_lang('CourseRequestCreationFailed'),
+                    'error',
+                    false
+                );
                 // Display the form.
                 $content = $form->return_form();
             }
         }
     } else {
-        $message = Display :: return_message(get_lang('CourseCodeAlreadyExists'), 'error', false);
+        $message = Display::return_message(
+            get_lang('CourseCodeAlreadyExists'),
+            'error',
+            false
+        );
         // Display the form.
         $content = $form->return_form();
     }
 } else {
     if (!$course_validation_feature) {
-        $message = Display :: return_message(get_lang('Explanation'));
+        $message = Display::return_message(get_lang('Explanation'));
     }
     // Display the form.
     $content = $form->returnForm();

+ 18 - 35
main/document/edit_document.php

@@ -195,8 +195,8 @@ if (!api_is_allowed_to_edit()) {
 
 if (isset($_POST['comment'])) {
 	// Fixing the path if it is wrong
-	$comment = Database::escape_string(trim($_POST['comment']));
-	$title = Database::escape_string(trim($_POST['title']));
+	$comment = trim($_POST['comment']);
+	$title = trim($_POST['title']);
 
     // Just in case see BT#3525
     if (empty($title)) {
@@ -208,10 +208,16 @@ if (isset($_POST['comment'])) {
 	}
 
     if (!empty($document_id)) {
-        $query = "UPDATE $dbTable SET comment='".$comment."', title='".$title."'
-        		WHERE c_id = $course_id AND id = ".$document_id;
-        Database::query($query);
-        $info_message = get_lang('fileModified');
+        $params = [
+            'comment' => $comment,
+            'title' => $title,
+        ];
+        Database::update(
+            $dbTable,
+            $params,
+            ['c_id = ? AND id = ?' => [$course_id, $document_id]]
+        );
+		Display::addFlash(Display::return_message(get_lang('fileModified')));
     }
 }
 
@@ -224,10 +230,6 @@ if ($is_allowed_to_edit) {
 		$content = isset($_POST['content']) ? trim(str_replace(array("\r", "\n"), '', stripslashes($_POST['content']))) : null;
 		$content = Security::remove_XSS($content, COURSEMANAGERLOWSECURITY);
 
-		/*if (!strstr($content, '/css/frames.css')) {
-			$content = str_replace('</title></head>', '</title><link rel="stylesheet" href="../css/frames.css" type="text/css" /></head>', $content);
-		}*/
-
         if ($dir == '/') {
             $dir = '';
         }
@@ -237,7 +239,7 @@ if ($is_allowed_to_edit) {
 		$read_only_flag = empty($read_only_flag) ? 0 : 1;
 
 		if (empty($filename)) {
-			$msgError = get_lang('NoFileName');
+            Display::addFlash(Display::return_message(get_lang('NoFileName'), 'warning'));
 		} else {
 
 		    $file_size = filesize($document_data['absolute_path']);
@@ -350,10 +352,10 @@ if ($is_allowed_to_edit) {
 							header('Location: document.php?id=' . $document_data['parent_id'] . '&' . api_get_cidreq() . ($is_certificate_mode?'&curdirpath=/certificates&selectcat=1':''));
 							exit;
 						} else {
-							$msgError = get_lang('Impossible');
+                            Display::addFlash(Display::return_message(get_lang('Impossible'), 'warning'));
 						}
 					} else {
-						$msgError = get_lang('Impossible');
+                        Display::addFlash(Display::return_message(get_lang('Impossible'), 'warning'));
 					}
 				} else {
 					if ($document_id) {
@@ -393,20 +395,6 @@ if (file_exists($document_data['absolute_path'])) {
 $nameTools = get_lang('EditDocument') . ': '.Security::remove_XSS($document_data['title']);
 Display::display_header($nameTools, 'Doc');
 
-if (isset($msgError)) {
-	Display::display_error_message($msgError);
-}
-
-if (isset($info_message)) {
-	Display::display_confirmation_message($info_message);
-	if (isset($_POST['origin'])) {
-		$slide_id = $_POST['origin_opt'];
-		$call_from_tool = $_POST['origin'];
-	}
-}
-
-// Owner
-
 $document_info = api_get_item_property_info(
     api_get_course_int_id(),
     'document',
@@ -573,19 +561,14 @@ function change_name($base_work_dir, $source_file, $rename_to, $dir, $doc)
 		}
 
 		update_db_info('update', $source_file, $new_full_file_name); // fileManage API
-		$name_changed = get_lang('ElRen');
-		$info_message = get_lang('fileModified');
+        Display::addFlash(Display::return_message(get_lang('fileModified')));
 
 		$GLOBALS['file_name'] = $rename_to;
 		$GLOBALS['doc'] = $rename_to;
 
-		return $info_message;
+		return true;
 	} else {
-		$dialogBox = get_lang('FileExists'); // TODO: This variable is not used.
-
-		/* Return to step 1 */
-		$rename = $source_file;
-		unset($source_file);
+        Display::addFlash(Display::return_message(get_lang('FileExists')));
 	}
 }
 

+ 59 - 36
main/dropbox/dropbox_class.inc.php

@@ -120,31 +120,38 @@ class Dropbox_Work
 		if ($this->isOldWork) {
 			$this->id = $res['id'];
 			$this->upload_date = $res['upload_date'];
-		    $sql = "UPDATE ".$dropbox_cnf["tbl_file"]." SET
-					filesize			= '".intval($this->filesize)."' ,
-					title 				= '".Database::escape_string($this->title)."',
-					description 		= '".Database::escape_string($this->description)."',
-					author 				= '".Database::escape_string($this->author)."',
-					last_upload_date 	= '".Database::escape_string($this->last_upload_date)."'
-					WHERE c_id = $course_id AND id = ".intval($this->id)."";
-			Database::query($sql);
+
+            $params = [
+                'filesize' => $this->filesize,
+                'title' => $this->title,
+                'description' => $this->description,
+                'author' => $this->author,
+                'last_upload_date' => $this->last_upload_date,
+                'session_id' => api_get_session_id(),
+            ];
+
+            Database::update(
+                $dropbox_cnf['tbl_file'],
+                $params,
+                ['c_id = ? AND id = ?' => [$course_id, $this->id]]
+            );
+
 		} else {
 			$this->upload_date = $this->last_upload_date;
-			$sql = "INSERT INTO ".$dropbox_cnf['tbl_file']." (c_id, uploader_id, filename, filesize, title, description, author, upload_date, last_upload_date, session_id)
-				VALUES ( $course_id,
-						'".intval($this->uploader_id)."'
-						, '".Database::escape_string($this->filename)."'
-						, '".intval($this->filesize)."'
-						, '".Database::escape_string($this->title)."'
-						, '".Database::escape_string($this->description)."'
-						, '".Database::escape_string($this->author)."'
-						, '".Database::escape_string($this->upload_date)."'
-						, '".Database::escape_string($this->last_upload_date)."'
-						, ".api_get_session_id()."
-						)";
-
-        	Database::query($sql);
-			$this->id = Database::insert_id(); // Get automatically inserted id
+			$params = [
+                'c_id' => $course_id,
+                'uploader_id' => $this->uploader_id,
+                'filename' => $this->filename,
+                'filesize' => $this->filesize,
+                'title' => $this->title,
+                'description' => $this->description,
+                'author' => $this->author,
+                'upload_date' => $this->upload_date,
+                'last_upload_date' => $this->last_upload_date,
+                'session_id' => api_get_session_id(),
+			];
+			$this->id = Database::insert($dropbox_cnf['tbl_file'], $params);
+
 			if ($this->id) {
 				$sql = "UPDATE ".$dropbox_cnf['tbl_file']." SET id = iid WHERE iid = {$this->id}";
 				Database::query($sql);
@@ -162,11 +169,6 @@ class Dropbox_Work
             $sql = "INSERT INTO ".$dropbox_cnf['tbl_person']." (c_id, file_id, user_id)
                     VALUES ($course_id, ".intval($this->id)." , ".intval($this->uploader_id).")";
             Database::query($sql);
-			/*$id = Database::insert_id();
-			if ($id) {
-				$sql = "UPDATE ".$dropbox_cnf['tbl_person']." SET id = iid WHERE iid = {$this->id}";
-				Database::query($sql);
-			}*/
         }
 	}
 
@@ -312,7 +314,7 @@ class Dropbox_SentWork extends Dropbox_Work
             $user_id = (int)$rec['id'];
 			$sql = "INSERT INTO $table_post (c_id, file_id, dest_user_id, session_id)
                     VALUES ($course_id, $file_id, $user_id, $session_id)";
-	        $result = Database::query($sql);
+	        Database::query($sql);
             // If work already exists no error is generated
 
             /**
@@ -325,7 +327,7 @@ class Dropbox_SentWork extends Dropbox_Work
 
                 // Do not add recipient in person table if mailing zip or just upload.
                 if (!$justSubmit) {
-                    $result = Database::query($sql);	// If work already exists no error is generated
+                    Database::query($sql);	// If work already exists no error is generated
                 }
             }
 
@@ -336,7 +338,15 @@ class Dropbox_SentWork extends Dropbox_Work
 			if (($recipid = $rec["id"]) > $dropbox_cnf['mailingIdBase']) {
 			    $recipid = $ownerid;  // mailing file recipient = mailing id, not a person
 			}
-			api_item_property_update($_course, TOOL_DROPBOX, $this->id, 'DropboxFileAdded', $ownerid, null, $recipid) ;
+            api_item_property_update(
+                $_course,
+                TOOL_DROPBOX,
+                $this->id,
+                'DropboxFileAdded',
+                $ownerid,
+                null,
+                $recipid
+            );
 		}
 	}
 
@@ -660,7 +670,8 @@ class Dropbox_Person
 		}
 		//$file_id = $this->sentWork[$index]->id;
 		// Delete entries in person table concerning sent works
-        $sql = "DELETE FROM ".$dropbox_cnf['tbl_person']." WHERE c_id = $course_id AND user_id='".$this->userId."' AND file_id='".$id."'";
+        $sql = "DELETE FROM ".$dropbox_cnf['tbl_person']."
+                WHERE c_id = $course_id AND user_id='".$this->userId."' AND file_id='".$id."'";
 		Database::query($sql);
 		removeMoreIfMailing($id);
 		removeUnusedFiles();	// Check for unused files
@@ -694,13 +705,25 @@ class Dropbox_Person
 			die(get_lang('GeneralError').' (code 221)');
 		}
 
-		$feedback_date = date('Y-m-d H:i:s', time());
+		$feedback_date = api_get_utc_datetime();
 		$this->receivedWork[$wi]->feedback_date = $feedback_date;
 		$this->receivedWork[$wi]->feedback = $text;
 
-		Database::query("UPDATE ".$dropbox_cnf['tbl_post']." SET feedback_date='".
-		    Database::escape_string($feedback_date)."', feedback='".Database::escape_string($text).
-		    "' WHERE c_id = $course_id AND dest_user_id='".$this->userId."' AND file_id='".$id."'");
+        $params = [
+            'feedback_date' => $feedback_date,
+            'feedback' => $text,
+        ];
+        Database::update(
+            $dropbox_cnf['tbl_post'],
+            $params,
+            [
+                'c_id = ? AND dest_user_id = ? AND file_id = ?' => [
+                    $course_id,
+                    $this->userId,
+                    $id,
+                ],
+            ]
+        );
 
 		// Update item_property table
 

+ 16 - 16
main/dropbox/dropbox_config.inc.php

@@ -4,29 +4,29 @@
 /**
  *  DATABASE TABLE VARIABLES
  */
-$dropbox_cnf['tbl_user']        = Database::get_main_table(TABLE_MAIN_USER);
+$dropbox_cnf['tbl_user'] = Database::get_main_table(TABLE_MAIN_USER);
 $dropbox_cnf['tbl_course_user'] = Database::get_main_table(TABLE_MAIN_COURSE_USER);
-$dropbox_cnf['tbl_post'] 		= Database::get_course_table(TABLE_DROPBOX_POST);
-$dropbox_cnf['tbl_file'] 		= Database::get_course_table(TABLE_DROPBOX_FILE);
-$dropbox_cnf['tbl_person'] 		= Database::get_course_table(TABLE_DROPBOX_PERSON);
-$dropbox_cnf['tbl_intro'] 		= Database::get_course_table(TABLE_TOOL_INTRO);
-$dropbox_cnf['tbl_category'] 	= Database::get_course_table(TABLE_DROPBOX_CATEGORY);
-$dropbox_cnf['tbl_feedback'] 	= Database::get_course_table(TABLE_DROPBOX_FEEDBACK);
+$dropbox_cnf['tbl_post'] = Database::get_course_table(TABLE_DROPBOX_POST);
+$dropbox_cnf['tbl_file'] = Database::get_course_table(TABLE_DROPBOX_FILE);
+$dropbox_cnf['tbl_person'] = Database::get_course_table(TABLE_DROPBOX_PERSON);
+$dropbox_cnf['tbl_intro'] = Database::get_course_table(TABLE_TOOL_INTRO);
+$dropbox_cnf['tbl_category'] = Database::get_course_table(TABLE_DROPBOX_CATEGORY);
+$dropbox_cnf['tbl_feedback'] = Database::get_course_table(TABLE_DROPBOX_FEEDBACK);
 
 /**
  *  INITIALISE OTHER VARIABLES & CONSTANTS
  */
-$dropbox_cnf['courseId'] 				= $_cid;
+$dropbox_cnf['courseId'] = $_cid;
 //path to dropbox subdir in course containing the uploaded files
-$dropbox_cnf['sysPath'] 				= api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/dropbox';
-$dropbox_cnf['webPath'] 				= api_get_path(WEB_COURSE_PATH) . $_course['path'] . '/dropbox';
+$dropbox_cnf['sysPath'] = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/dropbox';
+$dropbox_cnf['webPath'] = api_get_path(WEB_COURSE_PATH) . $_course['path'] . '/dropbox';
 
 //file size limit as imposed by the platform admin (see Chamilo Config Settings on the platform administration section)
-$dropbox_cnf['maxFilesize'] 			= api_get_setting('dropbox_max_filesize');
-$dropbox_cnf['allowOverwrite'] 			= api_string_2_boolean(api_get_setting('dropbox_allow_overwrite'));
-$dropbox_cnf['allowJustUpload'] 		= api_string_2_boolean(api_get_setting('dropbox_allow_just_upload'));
-$dropbox_cnf['allowStudentToStudent'] 	= api_string_2_boolean(api_get_setting('dropbox_allow_student_to_student'));
-$dropbox_cnf['allowGroup'] 				= api_string_2_boolean(api_get_setting('dropbox_allow_group'));
+$dropbox_cnf['maxFilesize'] = api_get_setting('dropbox_max_filesize');
+$dropbox_cnf['allowOverwrite'] = api_string_2_boolean(api_get_setting('dropbox_allow_overwrite'));
+$dropbox_cnf['allowJustUpload'] = api_string_2_boolean(api_get_setting('dropbox_allow_just_upload'));
+$dropbox_cnf['allowStudentToStudent'] = api_string_2_boolean(api_get_setting('dropbox_allow_student_to_student'));
+$dropbox_cnf['allowGroup'] = api_string_2_boolean(api_get_setting('dropbox_allow_group'));
 
 /**
  * MAILING VARIABLES
@@ -43,4 +43,4 @@ $dropbox_cnf['mailingWhereLOGINNAME'] = 'username';
 $dropbox_cnf['mailingFileRegexp'] = '/^(.+)\.\w{1,4}$/';
 $dropbox_cnf['sent_received_tabs'] = true;
 
-return $dropbox_cnf;
+return $dropbox_cnf;

+ 78 - 26
main/dropbox/dropbox_functions.inc.php

@@ -221,16 +221,20 @@ function store_move($id, $target, $part)
     if ((isset($id) AND $id != '') AND (isset($target) AND $target != '') AND (isset($part) AND $part != '')) {
 
         if ($part == 'received') {
-            $sql = "UPDATE ".$dropbox_cnf["tbl_post"]." SET cat_id = ".intval($target)."
-                        WHERE c_id = $course_id AND dest_user_id = ".intval($_user['user_id'])."
-                        AND file_id = ".intval($id)."";
+            $sql = "UPDATE ".$dropbox_cnf["tbl_post"]."
+                    SET cat_id = ".intval($target)."
+                    WHERE c_id = $course_id AND dest_user_id = ".intval($_user['user_id'])."
+                    AND file_id = ".intval($id)."";
             Database::query($sql);
             $return_message = get_lang('ReceivedFileMoved');
         }
         if ($part == 'sent') {
-            $sql = "UPDATE ".$dropbox_cnf["tbl_file"]." SET cat_id = ".intval($target)."
-                        WHERE c_id = $course_id AND uploader_id = ".intval($_user['user_id'])."
-                        AND id = ".intval($id)."";
+            $sql = "UPDATE ".$dropbox_cnf["tbl_file"]."
+                    SET cat_id = ".intval($target)."
+                    WHERE
+                        c_id = $course_id AND
+                        uploader_id = ".intval($_user['user_id'])." AND
+                        id = ".intval($id)."";
             Database::query($sql);
             $return_message = get_lang('SentFileMoved');
         }
@@ -380,23 +384,56 @@ function store_addcategory()
     if (!$_POST['edit_id']) {
         $session_id = api_get_session_id();
         // step 3a, we check if the category doesn't already exist
-        $sql = "SELECT * FROM ".$dropbox_cnf['tbl_category']." WHERE c_id = $course_id AND user_id='".$_user['user_id']."' AND cat_name='".Database::escape_string($_POST['category_name'])."' AND received='".$received."' AND sent='$sent' AND session_id='$session_id'";
+        $sql = "SELECT * FROM ".$dropbox_cnf['tbl_category']."
+                WHERE
+                    c_id = $course_id AND
+                    user_id='".$_user['user_id']."' AND
+                    cat_name='".Database::escape_string($_POST['category_name'])."' AND
+                    received='".$received."' AND
+                    sent='$sent' AND
+                    session_id='$session_id'";
         $result = Database::query($sql);
 
         // step 3b, we add the category if it does not exist yet.
         if (Database::num_rows($result) == 0) {
-            $sql = "INSERT INTO ".$dropbox_cnf['tbl_category']." (c_id, cat_name, received, sent, user_id, session_id)
-                    VALUES ($course_id, '".Database::escape_string($_POST['category_name'])."', '".Database::escape_string($received)."', '".Database::escape_string($sent)."', ".intval($_user['user_id']).", $session_id)";
-            Database::query($sql);
+            $params = [
+                'c_id' => $course_id,
+                'cat_name' => $_POST['category_name'],
+                'received' => $received,
+                'sent' => $sent,
+                'user_id' => $_user['user_id'],
+                'session_id' => $session_id,
+            ];
+            $id = Database::insert($dropbox_cnf['tbl_category'], $params);
+            if ($id) {
+                $sql = "UPDATE ".$dropbox_cnf['tbl_category']." SET cat_id = iid WHERE iid = $id";
+                Database::query($sql);
+            }
+
             return array('type' => 'confirmation', 'message' => get_lang('CategoryStored'));
         } else {
             return array('type' => 'error', 'message' => get_lang('CategoryAlreadyExistsEditIt'));
         }
     } else {
-        $sql = "UPDATE ".$dropbox_cnf['tbl_category']." SET cat_name='".Database::escape_string($_POST['category_name'])."', received='".Database::escape_string($received)."' , sent='".Database::escape_string($sent)."'
-                WHERE c_id = $course_id AND user_id = ".intval($_user['user_id'])."
-                AND cat_id = ".intval($_POST['edit_id'])."";
-        Database::query($sql);
+
+        $params = [
+            'cat_name' => $_POST['category_name'],
+            'received' => $received,
+            'sent' => $sent
+        ];
+
+        Database::update(
+            $dropbox_cnf['tbl_category'],
+            $params,
+            [
+                'c_id = ? AND user_id = ? AND cat_id = ?' => [
+                    $course_id,
+                    $_user['user_id'],
+                    $_POST['edit_id'],
+                ],
+            ]
+        );
+
         return array('type' => 'confirmation', 'message' => get_lang('CategoryModified'));
     }
 }
@@ -420,11 +457,15 @@ function display_addcategory_form($category_name = '', $id = '', $action)
 
     if (isset($id) AND $id != '') {
         // retrieve the category we are editing
-        $sql = "SELECT * FROM ".$dropbox_cnf['tbl_category']." WHERE c_id = $course_id AND cat_id = ".intval($id)."";
+        $sql = "SELECT * FROM ".$dropbox_cnf['tbl_category']."
+                WHERE c_id = $course_id AND cat_id = ".intval($id)."";
         $result = Database::query($sql);
         $row = Database::fetch_array($result);
 
-        if (empty($category_name)) { // after an edit with an error we do not want to return to the original name but the name we already modified. (happens when createinrecievedfiles AND createinsentfiles are not checked)
+        if (empty($category_name)) {
+            // after an edit with an error we do not want to return to the
+            // original name but the name we already modified.
+            // (happens when createinrecievedfiles AND createinsentfiles are not checked)
             $category_name = $row['cat_name'];
         }
         if ($row['received'] == '1') {
@@ -445,10 +486,8 @@ function display_addcategory_form($category_name = '', $id = '', $action)
 
     if ($action == 'editcategory') {
         $text = get_lang('ModifyCategory');
-        $class = 'save';
-    } elseif ($action == 'addreceivedcategory' or $action == 'addsentcategory') {
+    } elseif ($action == 'addreceivedcategory' || $action == 'addsentcategory') {
         $text = get_lang('CreateCategory');
-        $class = 'add';
     }
 
     $form = new FormValidator('add_new_category', 'post', api_get_self().'?view='.Security::remove_XSS($_GET['view']));
@@ -462,7 +501,7 @@ function display_addcategory_form($category_name = '', $id = '', $action)
 
     $form->addElement('text', 'category_name', get_lang('CategoryName'));
     $form->addRule('category_name', get_lang('ThisFieldIsRequired'), 'required');
-    $form->addElement('button', 'StoreCategory', $text);
+    $form->addButtonSave($text, 'StoreCategory');
 
     $defaults = array();
     $defaults['category_name'] = $category_name;
@@ -755,11 +794,13 @@ function removeMoreIfMailing($file_id)
     if ($res = Database::fetch_array($result)) {
         $mailingPseudoId = $res['dest_user_id'];
         if ($mailingPseudoId > dropbox_cnf('mailingIdBase')) {
-            $sql = "DELETE FROM " . dropbox_cnf('tbl_person') . " WHERE c_id = $course_id AND user_id='" . $mailingPseudoId . "'";
+            $sql = "DELETE FROM " . dropbox_cnf('tbl_person') . "
+                    WHERE c_id = $course_id AND user_id='" . $mailingPseudoId . "'";
             Database::query($sql);
 
-            $sql = "UPDATE " . dropbox_cnf('tbl_file') .
-                " SET uploader_id='" . api_get_user_id() . "' WHERE c_id = $course_id AND uploader_id='" . $mailingPseudoId . "'";
+            $sql = "UPDATE " . dropbox_cnf('tbl_file') ."
+                    SET uploader_id='" . api_get_user_id() . "'
+                    WHERE c_id = $course_id AND uploader_id='" . $mailingPseudoId . "'";
             Database::query($sql);
         }
     }
@@ -1073,9 +1114,20 @@ function store_feedback()
     if (empty($_POST['feedback'])) {
         return get_lang('PleaseTypeText');
     } else {
-        $sql="INSERT INTO ".$dropbox_cnf['tbl_feedback']." (c_id, file_id, author_user_id, feedback, feedback_date) VALUES
-              ($course_id, '".intval($_GET['id'])."','".api_get_user_id()."','".Database::escape_string($_POST['feedback'])."', '".api_get_utc_datetime()."')";
-        Database::query($sql);
+        $params = [
+            'c_id' => $course_id,
+            'file_id' => $_GET['id'],
+            'author_user_id' => api_get_user_id(),
+            'feedback' => $_POST['feedback'],
+            'feedback_date' => api_get_utc_datetime(),
+        ];
+
+        $id = Database::insert($dropbox_cnf['tbl_feedback'], $params);
+        if ($id) {
+            $sql = "UPDATE ".$dropbox_cnf['tbl_feedback']." SET feedback_id = iid WHERE iid = $id";
+            Database::query($sql);
+        }
+
         return get_lang('DropboxFeedbackStored');
     }
 }

+ 15 - 13
main/dropbox/index.php

@@ -71,15 +71,15 @@ if (isset($_POST['submitWork'])) {
 }
 
 // Display the form for adding a category
-if ($action == 'addreceivedcategory' or $action == 'addsentcategory') {
+if ($action == 'addreceivedcategory' || $action == 'addsentcategory') {
 	if (api_get_session_id() != 0 && !api_is_allowed_to_session_edit(false, true)) {
 		api_not_allowed();
 	}
-	display_addcategory_form($_POST['category_name'],'',$_GET['action']);
+	display_addcategory_form($_POST['category_name'], '', $_GET['action']);
 }
 
 // Editing a category: displaying the form
-if ($action == 'editcategory' and isset($_GET['id'])) {
+if ($action == 'editcategory' && isset($_GET['id'])) {
 	if (api_get_session_id() != 0 && !api_is_allowed_to_session_edit(false, true)) {
 		api_not_allowed();
 	}
@@ -108,7 +108,7 @@ if (isset($_POST['StoreCategory'])) {
 
 
 // Move a File
-if (($action == 'movesent' OR $action == 'movereceived') AND isset($_GET['move_id'])) {
+if (($action == 'movesent' || $action == 'movereceived') AND isset($_GET['move_id'])) {
 	if (api_get_session_id() != 0 && !api_is_allowed_to_session_edit(false, true)) {
 		api_not_allowed();
 	}
@@ -127,7 +127,7 @@ if (isset($_POST['do_move'])) {
 }
 
 // Delete a file
-if (($action == 'deletereceivedfile' OR $action == 'deletesentfile') AND isset($_GET['id']) AND is_numeric($_GET['id'])) {
+if (($action == 'deletereceivedfile' || $action == 'deletesentfile') AND isset($_GET['id']) AND is_numeric($_GET['id'])) {
 	if (api_get_session_id() != 0 && !api_is_allowed_to_session_edit(false, true)) {
 		api_not_allowed();
 	}
@@ -144,7 +144,7 @@ if (($action == 'deletereceivedfile' OR $action == 'deletesentfile') AND isset($
 }
 
 // Delete a category
-if (($action == 'deletereceivedcategory' OR $action == 'deletesentcategory') AND isset($_GET['id']) AND is_numeric($_GET['id'])) {
+if (($action == 'deletereceivedcategory' || $action == 'deletesentcategory') AND isset($_GET['id']) AND is_numeric($_GET['id'])) {
 	if (api_get_session_id() != 0 && !api_is_allowed_to_session_edit(false, true)) {
 		api_not_allowed();
 	}
@@ -205,7 +205,7 @@ if ($action != 'add') {
 	}
 
 	// ACTIONS
-	if ($view == 'received' OR !$dropbox_cnf['sent_received_tabs']) {
+	if ($view == 'received' || !$dropbox_cnf['sent_received_tabs']) {
 		//echo '<h3>'.get_lang('ReceivedFiles').'</h3>';
 
 		// This is for the categories
@@ -242,7 +242,7 @@ if ($action != 'add') {
 		}
 	}
 
-	if (!$view OR $view == 'sent' OR !$dropbox_cnf['sent_received_tabs']) {
+	if (!$view || $view == 'sent' || !$dropbox_cnf['sent_received_tabs']) {
 		// This is for the categories
 		if (isset($viewSentCategory) AND $viewSentCategory != '') {
 			$view_dropbox_category_sent = $viewSentCategory;
@@ -284,7 +284,7 @@ if ($action != 'add') {
 	if ($dropbox_cnf['sent_received_tabs']) {
 ?>
 <ul class="nav nav-tabs">
-    <li <?php if (!$view OR $view == 'sent') { echo 'class="active"'; } ?> >
+    <li <?php if (!$view || $view == 'sent') { echo 'class="active"'; } ?> >
         <a href="index.php?<?php echo api_get_cidreq(); ?>&view=sent" ><?php echo get_lang('SentFiles'); ?></a></li>
     <li <?php if ($view == 'received') { echo 'class="active"'; } ?> >
         <a href="index.php?<?php echo api_get_cidreq(); ?>&view=received"  ><?php echo get_lang('ReceivedFiles'); ?></a></li>
@@ -294,7 +294,7 @@ if ($action != 'add') {
 
 	/*	RECEIVED FILES */
 
-	if ($view == 'received' OR !$dropbox_cnf['sent_received_tabs']) {
+	if ($view == 'received' || !$dropbox_cnf['sent_received_tabs']) {
 		// This is for the categories
 		if (isset($viewReceivedCategory) AND $viewReceivedCategory != '') {
 			$view_dropbox_category_received = $viewReceivedCategory;
@@ -303,7 +303,8 @@ if ($action != 'add') {
 		}
 
 		// Object initialisation
-		$dropbox_person = new Dropbox_Person(api_get_user_id(), $is_courseAdmin, $is_courseTutor); // note: are the $is_courseAdmin and $is_courseTutor parameters needed????
+		$dropbox_person = new Dropbox_Person(api_get_user_id(), $is_courseAdmin, $is_courseTutor);
+		 // note: are the $is_courseAdmin and $is_courseTutor parameters needed????
 
 		// Constructing the array that contains the total number of feedback messages per document.
 		$number_feedback = get_total_number_feedback();
@@ -463,7 +464,7 @@ if ($action != 'add') {
 
 	/*	SENT FILES */
 
-	if (!$view OR $view == 'sent' OR !$dropbox_cnf['sent_received_tabs']) {
+	if (!$view || $view == 'sent' || !$dropbox_cnf['sent_received_tabs']) {
 		// This is for the categories
 		if (isset($viewSentCategory) AND $viewSentCategory != '') {
 			$view_dropbox_category_sent = $viewSentCategory;
@@ -576,7 +577,8 @@ if ($action != 'add') {
 				if ($category['sent'] == '1') {
 
                     $moveList[$category['cat_id']] = $category['cat_name'];
-					$dropbox_category_data[] = $category['cat_id']; // This is where the checkbox icon for the files appear.
+					$dropbox_category_data[] = $category['cat_id'];
+					// This is where the checkbox icon for the files appear.
 					$link_open = '<a href="'.api_get_self().'?'.api_get_cidreq().'&view_received_category='.$viewReceivedCategory.'&view_sent_category='.$category['cat_id'].'&view='.$view.'">';
 					$dropbox_category_data[] = $link_open.DocumentManager::build_document_icon_tag('folder', Security::remove_XSS($category['cat_name'])).'</a>';
 					$dropbox_category_data[] = '<a href="dropbox_download.php?'.api_get_cidreq().'&cat_id='.$category['cat_id'].'&action=downloadcategory&sent_received=sent">'.Display::return_icon('save_pack.png', get_lang('Save'), array('style' => 'float:right;'),ICON_SIZE_SMALL).'</a>'.$link_open.Security::remove_XSS($category['cat_name']).'</a>';

+ 12 - 28
main/exercice/answer.class.php

@@ -520,22 +520,19 @@ class Answer
         $hotspot_type
     ) {
         $answerTable = Database :: get_course_table(TABLE_QUIZ_ANSWER);
-        //$id = $this->getQuestionType() == FILL_IN_BLANKS ? $idAnswer[0] : Database::escape_string($position);
         $autoId = intval($autoId);
 
-        $sql = "UPDATE $answerTable SET
-                    answer = '".Database::escape_string($answer)."',
-                    comment = '".Database::escape_string($comment)."',
-                    correct = '".Database::escape_string($correct)."',
-                    ponderation = '".Database::escape_string($weighting)."',
-                    position = '".Database::escape_string($position)."',
-                    destination = '".Database::escape_string($destination)."',
-                    hotspot_coordinates = '".Database::escape_string($hotspot_coordinates)."',
-                    hotspot_type = '".Database::escape_string($hotspot_type)."'
-                WHERE
-                    id_auto = $autoId";
-
-        Database::query($sql);
+        $params = [
+            'answer' => $answer,
+            'comment' => $comment,
+            'correct' => $correct,
+            'ponderation' => $weighting,
+            'position' => $position,
+            'destination' => $destination,
+            'hotspot_coordinates' => $hotspot_coordinates,
+            'hotspot_type' => $hotspot_type,
+        ];
+        Database::update($answerTable, $params, ['id_auto= ?' => $autoId]);
 	}
 
 	/**
@@ -578,7 +575,7 @@ class Answer
                 ];
                 $autoId = Database::insert($answerTable, $params);
                 if ($autoId) {
-                    $sql = "UPDATE $answerTable SET id = id_auto WHERE id_auto = $autoId";
+                    $sql = "UPDATE $answerTable SET id = iid, id_auto = iid WHERE iid = $autoId";
                     Database::query($sql);
                 }
             } else {
@@ -622,19 +619,6 @@ class Answer
             }
         }
 
-        /*if (!empty($answerList)) {
-            foreach ($answerList as $autoId => $counterId) {
-                $sql = "UPDATE $answerTable SET answer = $autoId
-                        WHERE
-                            answer = $counterId AND
-                            c_id = $c_id AND
-                            question_id = $questionId
-                        ";
-
-                Database::query($sql);
-            }
-        }*/
-
         if (count($this->position) > $this->new_nbrAnswers) {
             $i = $this->new_nbrAnswers + 1;
             while ($this->position[$i]) {

+ 119 - 28
main/exercice/exercise.class.php

@@ -802,31 +802,41 @@ class Exercise
                 $end_time = '0000-00-00 00:00:00';
             }
 
-            $sql = "UPDATE $TBL_EXERCISES SET
-				    title='".Database::escape_string($exercise)."',
-					description='".Database::escape_string($description)."'";
+            $params = [
+                'title' => $exercise,
+                'description' => $description,
+            ];
 
+            $paramsExtra = [];
             if ($type_e != 'simple') {
-                $sql .= ",sound='".Database::escape_string($sound)."',
-					type           = ".intval($type).",
-					random         = ".intval($random).",
-					random_answers = ".intval($random_answers).",
-					active         = ".intval($active).",
-					feedback_type  = ".intval($feedback_type).",
-					start_time     = '$start_time',
-					end_time       = '$end_time',
-					max_attempt    = ".intval($attempts).",
-     			    expired_time   = ".intval($expired_time).",
-         			propagate_neg  = ".intval($propagate_neg).",
-         			review_answers = ".intval($review_answers).",
-        	        random_by_category= ".intval($randomByCat).",
-        	        text_when_finished = '".Database::escape_string($text_when_finished)."',
-        	        display_category_name = ".intval($display_category_name).",
-                    pass_percentage = ".intval($pass_percentage).",
-					results_disabled= ".intval($results_disabled)."";
+                $paramsExtra = [
+                    'sound' => $sound,
+                    'type' => $type,
+                    'random' => $random,
+                    'random_answers' => $random_answers,
+                    'active' => $active,
+                    'feedback_type' => $feedback_type,
+                    'start_time' => $start_time,
+                    'end_time' => $end_time,
+                    'max_attempt' => $attempts,
+                    'expired_time' => $expired_time,
+                    'propagate_neg' => $propagate_neg,
+                    'review_answers' => $review_answers,
+                    'random_by_category' => $randomByCat,
+                    'text_when_finished' => $text_when_finished,
+                    'display_category_name' => $display_category_name,
+                    'pass_percentage' => $pass_percentage,
+                    'results_disabled' => $results_disabled,
+                ];
             }
-            $sql .= " WHERE c_id = ".$this->course_id." AND id = ".intval($id)."";
-            Database::query($sql);
+
+            $params = array_merge($params, $paramsExtra);
+
+            Database::update(
+                $TBL_EXERCISES,
+                $params,
+                ['c_id = ? AND id = ?' => [$this->course_id, $id]]
+            );
 
             // update into the item_property table
             api_item_property_update(
@@ -849,13 +859,13 @@ class Exercise
             // the Quiz is saved too, with an $id and api_get_utc_datetime() is done.
             // If we do it now, it will be done twice (cf. https://support.chamilo.org/issues/6586)
             if (!empty($this->start_time) && $this->start_time != '0000-00-00 00:00:00') {
-                $start_time = Database::escape_string($this->start_time);
+                $start_time = $this->start_time;
             } else {
                 $start_time = '0000-00-00 00:00:00';
             }
 
             if (!empty($this->end_time) && $this->end_time != '0000-00-00 00:00:00') {
-                $end_time 	= Database::escape_string(($this->end_time));
+                $end_time = $this->end_time;
             } else {
                 $end_time = '0000-00-00 00:00:00';
             }
@@ -2487,13 +2497,93 @@ class Exercise
                     break;
                 // for fill in the blanks
                 case FILL_IN_BLANKS:
+
+                    // insert the student result in the track_e_attempt table, field answer
+                    // $answer is the answer like in the c_quiz_answer table for the question
+                    // student datas are choice[]
+
+                    $listCorrectAnswers = FillBlanks::getAnswerInfo($answer);
+                    $switchableAnswerSet = $listCorrectAnswers["switchable"];
+                    $answerWeighting = $listCorrectAnswers["tabweighting"];
+                    // user choices is an array $choice
+
+                    // get existing user data in n the BDD
+                    if ($from_database) {
+                        $queryfill = "SELECT answer
+                                      FROM $TBL_TRACK_ATTEMPT
+                                      WHERE exe_id = $exeId
+                                      AND question_id= ".intval($questionId);
+                        $resfill = Database::query($queryfill);
+                        $str = Database::result($resfill, 0, 'answer');
+
+                        $listStudentResults = FillBlanks::getAnswerInfo($str, true);
+                        $choice = $listStudentResults['studentanswer'];
+                    }
+
+                    // loop other all blanks words
+                    if (!$switchableAnswerSet) {
+                        // not switchable answer, must be in the same place than teacher order
+                        for ($i=0; $i < count($listCorrectAnswers['tabwords']); $i++) {
+                            $studentAnswer = isset($choice[$i]) ? trim($choice[$i]) : '';
+
+                            // This value is the user input, not escaped while correct answer is escaped by fckeditor
+                            // Works with cyrillic alphabet and when using ">" chars see #7718 #7610 #7618
+                            if (!$from_database) {
+                                $studentAnswer = htmlentities(
+                                    api_utf8_encode($studentAnswer)
+                                );
+                            }
+
+                            $correctAnswer = $listCorrectAnswers['tabwords'][$i];
+                            $isAnswerCorrect = 0;
+                            if (FillBlanks::isGoodStudentAnswer($studentAnswer, $correctAnswer)) {
+                                // gives the related weighting to the student
+                                $questionScore += $answerWeighting[$i];
+                                // increments total score
+                                $totalScore += $answerWeighting[$i];
+                                $isAnswerCorrect = 1;
+                            }
+                            $listCorrectAnswers['studentanswer'][$i] = $studentAnswer;
+                            $listCorrectAnswers['studentscore'][$i] = $isAnswerCorrect;
+                        }
+                    } else {
+                        // switchable answer
+                        $listStudentAnswerTemp = $choice;
+
+                        $listTeacherAnswerTemp = $listCorrectAnswers['tabwords'];
+                        $listBadAnswerIndice = array();
+                        // for every teacher answer, check if there is a student answer
+                        for ($i=0; $i < count($listStudentAnswerTemp); $i++) {
+                            $studentAnswer = trim($listStudentAnswerTemp[$i]);
+                            $found = false;
+                            for ($j=0; $j < count($listTeacherAnswerTemp); $j++) {
+                                $correctAnswer = $listTeacherAnswerTemp[$j];
+                                if (!$found) {
+                                    if (FillBlanks::isGoodStudentAnswer($studentAnswer, $correctAnswer)) {
+                                        $questionScore += $answerWeighting[$i];
+                                        $totalScore += $answerWeighting[$i];
+                                        $listTeacherAnswerTemp[$j] = "";
+                                        $found = true;
+                                    }
+                                }
+                            }
+                            $listCorrectAnswers['studentanswer'][$i] = $studentAnswer;
+                            if (!$found) {
+                                $listCorrectAnswers['studentscore'][$i] = 0;
+                            } else {
+                                $listCorrectAnswers['studentscore'][$i] = 1;
+                            }
+                        }
+                    }
+                    $answer = FillBlanks::getAnswerInStudentAttempt($listCorrectAnswers);
+
                     // the question is encoded like this
                     // [A] B [C] D [E] F::10,10,10@1
                     // number 1 before the "@" means that is a switchable fill in blank question
                     // [A] B [C] D [E] F::10,10,10@ or  [A] B [C] D [E] F::10,10,10
                     // means that is a normal fill blank question
                     // first we explode the "::"
-
+                    /*
                     $pre_array = explode('::', $answer);
                     // is switchable fill blank or not
                     $last = count($pre_array) - 1;
@@ -2634,7 +2724,7 @@ class Exercise
                         if (isset($real_text[$i +1])) {
                             $answer .= $real_text[$i + 1];
                         }
-                    }
+                    }*/
                     break;
                 // for calculated answer
                 case CALCULATED_ANSWER:
@@ -3060,7 +3150,7 @@ class Exercise
                             //}
                         } elseif ($answerType == FILL_IN_BLANKS) {
                             //if ($origin!='learnpath') {
-                            ExerciseShowFunctions::display_fill_in_blanks_answer($feedback_type, $answer,0,0);
+                            ExerciseShowFunctions::display_fill_in_blanks_answer($feedback_type, $answer,0,0, $results_disabled);
                             //	}
                         } elseif ($answerType == CALCULATED_ANSWER) {
                             //if ($origin!='learnpath') {
@@ -3380,7 +3470,8 @@ class Exercise
                                 $feedback_type,
                                 $answer,
                                 $exeId,
-                                $questionId
+                                $questionId,
+                                $results_disabled
                             );
                             break;
                         case CALCULATED_ANSWER:

+ 0 - 10
main/exercice/exercise_result.php

@@ -135,16 +135,6 @@ Display :: display_normal_message(get_lang('Saved').'<br />',false);
 // Display and save questions
 ExerciseLib::display_question_list_by_attempt($objExercise, $exe_id, true);
 
-//If is not valid
-
-/*
-$session_control_key = ExerciseLib::get_session_time_control_key($objExercise->id, $learnpath_id, $learnpath_item_id);
-if (isset($session_control_key) && !ExerciseLib::exercise_time_control_is_valid($objExercise->id, $learnpath_id, $learnpath_item_id)) {
-	$TBL_TRACK_ATTEMPT		= Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
-	$sql_fraud = "UPDATE $TBL_TRACK_ATTEMPT SET answer = 0, marks = 0, position = 0 WHERE exe_id = $exe_id ";
-	Database::query($sql_fraud);
-}*/
-
 //Unset session for clock time
 ExerciseLib::exercise_time_control_delete($objExercise->id, $learnpath_id, $learnpath_item_id);
 ExerciseLib::delete_chat_exercise_session($exe_id);

+ 876 - 106
main/exercice/fill_blanks.class.php

@@ -10,8 +10,12 @@
  **/
 class FillBlanks extends Question
 {
-	static $typePicture = 'fill_in_blanks.png';
-	static $explanationLangVar = 'FillBlanks';
+    static $typePicture = 'fill_in_blanks.png';
+    static $explanationLangVar = 'FillBlanks';
+
+    const FILL_THE_BLANK_STANDARD = 0;
+    const FILL_THE_BLANK_MENU = 1;
+    const FILL_THE_BLANK_SEVERAL_ANSWER = 2;
 
     /**
      * Constructor
@@ -19,50 +23,45 @@ class FillBlanks extends Question
     public function __construct()
     {
         parent::__construct();
-        $this -> type = FILL_IN_BLANKS;
-        $this -> isContent = $this-> getIsContent();
+        $this->type = FILL_IN_BLANKS;
+        $this->isContent = $this->getIsContent();
     }
 
     /**
      * function which redefines Question::createAnswersForm
-     * @param the formvalidator instance
+     * @param FormValidator $form
      */
-    function createAnswersForm($form)
+    public function createAnswersForm($form)
     {
+        $fillBlanksAllowedSeparator = self::getAllowedSeparator();
+
         $defaults = array();
 
         if (!empty($this->id)) {
-            $objAnswer = new Answer($this->id);
-
-            // the question is encoded like this
-            // [A] B [C] D [E] F::10,10,10@1
-            // number 1 before the "@" means that is a switchable fill in blank question
-            // [A] B [C] D [E] F::10,10,10@ or  [A] B [C] D [E] F::10,10,10
-            // means that is a normal fill blank question
-
-            $pre_array = explode('::', $objAnswer->selectAnswer(1));
+            $objectAnswer = new Answer($this->id);
+            $answer = $objectAnswer->selectAnswer(1);
+            $listAnswersInfo = FillBlanks::getAnswerInfo($answer);
 
-            //make sure we only take the last bit to find special marks
-            $sz = count($pre_array);
-            $is_set_switchable = explode('@', $pre_array[$sz-1]);
-
-            if ($is_set_switchable[1]) {
+            if ($listAnswersInfo["switchable"]) {
                 $defaults['multiple_answer'] = 1;
             } else {
                 $defaults['multiple_answer'] = 0;
             }
-
-            //Take the complete string except after the last '::'
-
-            $defaults['answer'] = '';
-            for ($i=0; $i<($sz-1); $i++) {
-                $defaults['answer'] .= $pre_array[$i];
-            }
-            $a_weightings = explode(',', $is_set_switchable[0]);
+            //take the complete string except after the last '::'
+            $defaults['answer'] = $listAnswersInfo["text"];
+            $defaults['select_separator'] = $listAnswersInfo["blankseparatornumber"];
+            $blanksepartornumber = $listAnswersInfo["blankseparatornumber"];
         } else {
             $defaults['answer'] = get_lang('DefaultTextInBlanks');
+            $defaults['select_separator'] = 0;
+            $blanksepartornumber = 0;
         }
 
+        $blankSeparatortStart = self::getStartSeparator($blanksepartornumber);
+        $blankSeparatortEnd = self::getEndSeparator($blanksepartornumber);
+        $blankSeparatortStartRegexp = self::escapeForRegexp($blankSeparatortStart);
+        $blankSeparatortEndRegexp = self::escapeForRegexp($blankSeparatortEnd);
+
         $setValues = null;
 
         if (isset($a_weightings) && count($a_weightings) > 0) {
@@ -70,8 +69,13 @@ class FillBlanks extends Question
                 $setValues .= 'document.getElementById("weighting['.$i.']").value = "'.$weighting.'";';
             }
         }
-		// javascript
-		echo '<script>
+        // javascript
+        echo '<script>
+
+            var blankSeparatortStart = "'.$blankSeparatortStart.'";
+            var blankSeparatortEnd = "'.$blankSeparatortEnd.'";
+            var blankSeparatortStartRegexp = getBlankSeparatorRegexp(blankSeparatortStart);
+            var blankSeparatortEndRegexp = getBlankSeparatorRegexp(blankSeparatortEnd);
 
             CKEDITOR.on("instanceCreated", function(e) {
                 if (e.editor.name === "answer") {
@@ -81,108 +85,327 @@ class FillBlanks extends Question
 
             var firstTime = true;
 
-            function updateBlanks(e) {
+            function updateBlanks()
+            {
                 if (firstTime) {
-                    field = document.getElementById("answer");
+                    var field = document.getElementById("answer");
                     var answer = field.value;
                 } else {
-                    var answer = e.editor.getData();
+                    var answer = CKEDITOR.instances["answer"].getData();
                 }
 
-                var blanks = answer.match(/\[[^\]]*\]/g);
-                var fields = "<div class=\"form-group\"><label class=\" col-sm-2 control-label\">'.get_lang('Weighting').'</label><div class=\"col-sm-8\"><table>";
-                if (blanks!=null) {
-                    for (i=0 ; i<blanks.length ; i++){
-                        if (document.getElementById("weighting["+i+"]"))
-                            value = document.getElementById("weighting["+i+"]").value;
-                        else
-                            value = "10";
-                        fields += "<tr><td><label>"+blanks[i]+"</label></td><td><input class=\"form-control\"style=\"margin-left: 0em;\" size=\"5\" value=\""+value+"\" type=\"text\" id=\"weighting["+i+"]\" name=\"weighting["+i+"]\" /></td></tr>";
+                // disable the save button, if not blanks have been created
+                $("button").attr("disabled", "disabled");
+                $("#defineoneblank").show();
+
+                var blanksRegexp = "/"+blankSeparatortStartRegexp+"[^"+blankSeparatortStartRegexp+"]*"+blankSeparatortEndRegexp+"/g";
+
+                var blanks = answer.match(eval(blanksRegexp));
+                var fields = "<div class=\"form-group \">";
+                fields += "<label class=\"col-sm-2 control-label\">'.get_lang('Weighting').'</label>";
+                fields += "<div class=\"col-sm-8\">";
+                fields += "<table>";
+                fields += "<tr><th style=\"padding:0 20px\">'.get_lang("WordTofind").'</th><th style=\"padding:0 20px\">'.get_lang("QuestionWeighting").'</th><th style=\"padding:0 20px\">'.get_lang("BlankInputSize").'</th></tr>";
+
+                if (blanks != null) {
+                    for (var i=0 ; i < blanks.length ; i++){
+                        // remove forbidden characters that causes bugs
+                        blanks[i] = removeForbiddenChars(blanks[i]);
+                        // trim blanks between brackets
+                        blanks[i] = trimBlanksBetweenSeparator(blanks[i], blankSeparatortStart, blankSeparatortEnd);
+
+                        // if the word is empty []
+                        if (blanks[i] == blankSeparatortStartRegexp+blankSeparatortEndRegexp) {
+                            break;
+                        }
+                        // get input size
+                        var lainputsize = 200;
+                        if ($("#samplesize\\\["+i+"\\\]").width()) {
+                            lainputsize = $("#samplesize\\\["+i+"\\\]").width();
+                        }
+
+                        if (document.getElementById("weighting["+i+"]")) {
+                            var value = document.getElementById("weighting["+i+"]").value;
+                        } else {
+                            var value = "10";
+                        }
+                        fields += "<tr>";
+                        fields += "<td>"+blanks[i]+"</td>";
+                        fields += "<td><input style=\"width:35px\" value=\""+value+"\" type=\"text\" id=\"weighting["+i+"]\" name=\"weighting["+i+"]\" /></td>";
+                        fields += "<td>";
+                        fields += "<input type=\"button\" value=\"-\" onclick=\"changeInputSize(-1, "+i+")\">";
+                        fields += "<input type=\"button\" value=\"+\" onclick=\"changeInputSize(1, "+i+")\">";
+                        fields += "<input value=\""+blanks[i].substr(1, blanks[i].length - 2)+"\" style=\"width:"+lainputsize+"px\" disabled=disabled id=\"samplesize["+i+"]\"/>";
+                        fields += "<input type=\"hidden\" id=\"sizeofinput["+i+"]\" name=\"sizeofinput["+i+"]\" value=\""+lainputsize+"\" \"/>";
+                        fields += "</td>";
+                        fields += "</tr>";
+                        // enable the save button
+                        $("button").removeAttr("disabled");
+                        $("#defineoneblank").hide();
                     }
                 }
                 document.getElementById("blanks_weighting").innerHTML = fields + "</table></div></div>";
                 if (firstTime) {
                     firstTime = false;
-                    '.$setValues.'
-			    }
+                ';
+
+        if (isset($listAnswersInfo) && count($listAnswersInfo["tabweighting"]) > 0) {
+
+            foreach ($listAnswersInfo["tabweighting"] as $i => $weighting) {
+                if (!empty($i)) {
+                    echo 'document.getElementById("weighting['.$i.']").value = "'.$weighting.'";';
+                }
+            }
+            foreach ($listAnswersInfo["tabinputsize"] as $i => $sizeOfInput) {
+                if (!empty($i)) {
+                    echo 'document.getElementById("sizeofinput['.$i.']").value = "'.$sizeOfInput.'";';
+                    echo '$("#samplesize\\\['.$i.'\\\]").width('.$sizeOfInput.');';
+                }
             }
+        }
 
+        echo '}
+            }
             window.onload = updateBlanks;
-		</script>';
 
-		// answer
-		$form->addElement('label', null, '<br /><br />'.get_lang('TypeTextBelow').', '.get_lang('And').' '.get_lang('UseTagForBlank'));
-		$form->addElement(
+            function getInputSize() {
+                var outTabSize = new Array();
+                $("input").each(function() {
+                    if ($(this).attr("id") && $(this).attr("id").match(/samplesize/)) {
+                        var tabidnum = $(this).attr("id").match(/\d+/);
+                        var idnum = tabidnum[0];
+                        var thewidth = $(this).next().attr("value");
+                        tabInputSize[idnum] = thewidth;
+                    }
+                });
+            }
+
+            function changeInputSize(inCoef, inIdNum)
+            {
+                var currentWidth = $("#samplesize\\\["+inIdNum+"\\\]").width();
+                var newWidth = currentWidth + inCoef * 20;
+                newWidth = Math.max(20, newWidth);
+                newWidth = Math.min(newWidth, 600);
+                $("#samplesize\\\["+inIdNum+"\\\]").width(newWidth);
+                $("#sizeofinput\\\["+inIdNum+"\\\]").attr("value", newWidth);
+            }
+
+            function removeForbiddenChars(inTxt) {
+                outTxt = inTxt;
+
+                outTxt = outTxt.replace(/&quot;/g, ""); // remove the   char
+                outTxt = outTxt.replace(/\x22/g, ""); // remove the   char
+                outTxt = outTxt.replace(/"/g, ""); // remove the   char
+                outTxt = outTxt.replace(/\\\\/g, ""); // remove the \ char
+                outTxt = outTxt.replace(/&nbsp;/g, " ");
+                outTxt = outTxt.replace(/^ +/, "");
+                outTxt = outTxt.replace(/ +$/, "");
+                return outTxt;
+            }
+
+            function changeBlankSeparator()
+            {
+                var separatorNumber = $("#select_separator").val();
+                var tabSeparator = getSeparatorFromNumber(separatorNumber);
+                blankSeparatortStart = tabSeparator[0];
+                blankSeparatortEnd = tabSeparator[1];
+                blankSeparatortStartRegexp = getBlankSeparatorRegexp(blankSeparatortStart);
+                blankSeparatortEndRegexp = getBlankSeparatorRegexp(blankSeparatortEnd);
+                updateBlanks();
+            }
+
+            // this function is the same than the PHP one
+            // if modify it modify the php one escapeForRegexp
+            function getBlankSeparatorRegexp(inTxt)
+            {
+                var tabSpecialChar = new Array(".", "+", "*", "?", "[", "^", "]", "$", "(", ")",
+                    "{", "}", "=", "!", "<", ">", "|", ":", "-", ")");
+                for (var i=0; i < tabSpecialChar.length; i++) {
+                    if (inTxt == tabSpecialChar[i]) {
+                        return "\\\"+inTxt;
+                    }
+                }
+                return inTxt;
+            }
+
+            // this function is the same than the PHP one
+            // if modify it modify the php one getAllowedSeparator
+            function getSeparatorFromNumber(innumber)
+            {
+                tabSeparator = new Array();
+                tabSeparator[0] = new Array("[", "]");
+                tabSeparator[1] = new Array("{", "}");
+                tabSeparator[2] = new Array("(", ")");
+                tabSeparator[3] = new Array("*", "*");
+                tabSeparator[4] = new Array("#", "#");
+                tabSeparator[5] = new Array("%", "%");
+                tabSeparator[6] = new Array("$", "$");
+                return tabSeparator[innumber];
+            }
+
+            function trimBlanksBetweenSeparator(inTxt, inSeparatorStart, inSeparatorEnd)
+            {
+                // blankSeparatortStartRegexp
+                // blankSeparatortEndRegexp
+                var result = inTxt
+                result = result.replace(inSeparatorStart, "");
+                result = result.replace(inSeparatorEnd, "");
+                result = result.trim();
+                return inSeparatorStart+result+inSeparatorEnd;
+            }
+        </script>';
+
+        // answer
+        $form->addElement('label', null, '<br /><br />'.get_lang('TypeTextBelow').', '.get_lang('And').' '.get_lang('UseTagForBlank'));
+        $form->addElement(
             'html_editor',
             'answer',
             '<img src="../img/fill_field.png">',
-            array(
-                'id' => 'answer',
-                'onkeyup' => '"javascript: updateBlanks(this);"'
-            ),
-            array('ToolbarSet' => 'TestQuestionDescription', 'Height' => '350'));
+            ['id' => 'answer', 'onkeyup' => "javascript: updateBlanks(this);"],
+            array('ToolbarSet' => 'TestQuestionDescription')
+        );
+        $form->addRule('answer',get_lang('GiveText'),'required');
 
-		$form->addRule('answer', get_lang('GiveText'),'required');
-		$form->addRule('answer', get_lang('DefineBlanks'),'regex','/\[.*\]/');
+        //added multiple answers
+        $form->addElement('checkbox','multiple_answer','', get_lang('FillInBlankSwitchable'));
+        $form->addElement('select', 'select_separator', get_lang("SelectFillTheBlankSeparator"), self::getAllowedSeparatorForSelect(), ' id="select_separator"   style="width:150px" onchange="changeBlankSeparator()" ');
+        $form->addElement('label', null, '<input type="button" onclick="updateBlanks()" value="'.get_lang('RefreshBlanks').'" class="btn" />');
+        $form->addElement('html','<div id="blanks_weighting"></div>');
 
-		//added multiple answers
-		$form->addElement('checkbox', 'multiple_answer','', get_lang('FillInBlankSwitchable'));
+        global $text;
+        // setting the save button here and not in the question class.php
+        $form->addElement('html','<div id="defineoneblank" style="color:#D04A66; margin-left:160px">'.get_lang('DefineBlanks').'</div>');
+        $form->addButtonSave($text, 'submitQuestion');
 
-		$form->addElement('html', '<div id="blanks_weighting"></div>');
+        if (!empty($this->id)) {
+            $form->setDefaults($defaults);
+        } else {
+            if ($this->isContent == 1) {
+                $form->setDefaults($defaults);
+            }
+        }
+    }
 
-		global $text, $class;
-		// setting the save button here and not in the question class.php
-        $form->addButtonSave($text, 'submitQuestion');
+    /**
+     * abstract function which creates the form to create / edit the answers of the question
+     * @param FormValidator $form
+     */
+    public function processAnswersCreation($form)
+    {
+        global $charset;
+
+        $answer = $form->getSubmitValue('answer');
+        //var_dump($answer);
+        // Due the fckeditor transform the elements to their HTML value
+        $answer = api_html_entity_decode($answer, ENT_QUOTES, $charset);
 
-		if (!empty($this->id)) {
-			$form -> setDefaults($defaults);
-		} else {
-			if ($this->isContent == 1) {
-				$form->setDefaults($defaults);
-			}
-		}
-	}
-
-	/**
-	 * abstract function which creates the form to create / edit the answers of the question
-	 * @param FormValidator $form
-	 */
-	function processAnswersCreation($form)
-	{
-		global $charset;
-		$answer = $form->getSubmitValue('answer');
-
-		//remove the :: eventually written by the user
+        // remove the :: eventually written by the user
         $answer = str_replace('::', '', $answer);
 
+        // remove starting and ending space and &nbsp;
+
+        $answer = api_preg_replace("/\xc2\xa0/", " ", $answer);
+
+        // start and end separator
+
+        $blankStartSeparator = self::getStartSeparator($form->getSubmitValue('select_separator'));
+        $blankEndSeparator = self::getEndSeparator($form->getSubmitValue('select_separator'));
+        $blankStartSeparatorRegexp = self::escapeForRegexp($blankStartSeparator);
+        $blankEndSeparatorRegexp = self::escapeForRegexp($blankEndSeparator);
+
+        // remove spaces at the beginning and the end of text in square brackets
+        $answer = preg_replace_callback(
+            "/".$blankStartSeparatorRegexp."[^]]+".$blankEndSeparatorRegexp."/",
+            function ($matches) use ($blankStartSeparator, $blankEndSeparator) {
+                $matchingResult = $matches[0];
+                $matchingResult = trim($matchingResult, $blankStartSeparator);
+                $matchingResult = trim($matchingResult, $blankEndSeparator);
+                $matchingResult = trim($matchingResult);
+                // remove forbidden chars
+                $matchingResult = str_replace("/\\/", "", $matchingResult);
+                $matchingResult = str_replace('/"/', "", $matchingResult);
+
+                return $blankStartSeparator.$matchingResult.$blankEndSeparator;
+            },
+            $answer
+        );
+
         // get the blanks weightings
-        $nb = preg_match_all('/\[[^\]]*\]/', $answer, $blanks);
+        $nb = preg_match_all(
+            '/'.$blankStartSeparatorRegexp.'[^'.$blankStartSeparatorRegexp.']*'.$blankEndSeparatorRegexp.'/',
+            $answer,
+            $blanks
+        );
+
         if (isset($_GET['editQuestion'])) {
-            $this ->weighting = 0;
-        }
-
-		if ($nb > 0) {
-		  	$answer .= '::';
-			for ($i=0 ; $i < $nb; ++$i) {
-                $blankItem = $blanks[0][$i];
-                $replace = array("[", "]");
-                $newBlankItem = str_replace($replace, "", $blankItem);
-                $newBlankItem = "[".trim($newBlankItem)."]";
-                $answer = str_replace($blankItem, $newBlankItem, $answer);
-                $answer .= $form->getSubmitValue('weighting['.$i.']').',';
+            $this->weighting = 0;
+        }
+
+        /* if we have some [tobefound] in the text
+        build the string to save the following in the answers table
+        <p>I use a [computer] and a [pen].</p>
+        becomes
+        <p>I use a [computer] and a [pen].</p>::100,50:100,50@1
+                                              ++++++++-------**
+                                                --- -- --- -- -
+                                                  A B  (C) (D)(E)
+        +++++++ : required, weighting of each words
+        ------- : optional, input width to display, 200 if not present
+        ** : equal @1 if "Allow answers order switches" has been checked, @ otherwise
+        A : weighting for the word [computer]
+        B : weighting for the word [pen]
+        C : input width for the word [computer]
+        D : input width for the word [pen]
+        E : equal @1 if "Allow answers order switches" has been checked, @ otherwise
+        */
+        if ($nb > 0) {
+            $answer .= '::';
+            // weighting
+            for ($i=0; $i < $nb; ++$i) {
+                // enter the weighting of word $i
+                $answer .= $form->getSubmitValue('weighting['.$i.']');
+                // not the last word, add ","
+                if ($i != $nb - 1) {
+                    $answer .= ",";
+                }
+                // calculate the global weighting for the question
                 $this -> weighting += $form->getSubmitValue('weighting['.$i.']');
             }
-            $answer = api_substr($answer, 0, -1);
-		}
 
-		$is_multiple = $form->getSubmitValue('multiple_answer');
-		$answer.= '@'.$is_multiple;
+            // input width
+            $answer .= ":";
+            for ($i=0; $i < $nb; ++$i) {
+                // enter the width of input for word $i
+                $answer .= $form->getSubmitValue('sizeofinput['.$i.']');
+                // not the last word, add ","
+                if ($i != $nb - 1) {
+                    $answer .= ",";
+                }
+            }
+        }
+
+        // write the blank separator code number
+        // see function getAllowedSeparator
+        /*
+            0 [...]
+            1 {...}
+            2 (...)
+            3 *...*
+            4 #...#
+            5 %...%
+            6 $...$
+         */
+        $answer .= ":".$form->getSubmitValue('select_separator');
 
-		$this->save();
+        // Allow answers order switches
+        $is_multiple = $form -> getSubmitValue('multiple_answer');
+        $answer.= '@'.$is_multiple;
+
+        $this->save();
         $objAnswer = new Answer($this->id);
-        $objAnswer->createAnswer($answer, 0, '', 0, '1');
+        $objAnswer->createAnswer($answer, 0, '', 0, 1);
         $objAnswer->save();
-	}
+    }
 
     /**
      * @param null $feedback_type
@@ -190,13 +413,560 @@ class FillBlanks extends Question
      * @param null $score
      * @return null|string
      */
-    function return_header($feedback_type = null, $counter = null, $score = null)
+    public function return_header($feedback_type = null, $counter = null, $score = null)
     {
-	    $header = parent::return_header($feedback_type, $counter, $score);
-	    $header .= '<table class="'.$this->question_table_class .'">
-			<tr>
+        $header = parent::return_header($feedback_type, $counter, $score);
+        $header .= '<table class="'.$this->question_table_class .'">
+            <tr>
                 <th>'.get_lang("Answer").'</th>
-			</tr>';
+            </tr>';
         return $header;
-	}
+    }
+
+    /**
+     * @param $separatorStartRegexp
+     * @param $separatorEndRegexp
+     * @param $correctItemRegexp
+     * @param $questionId
+     * @param $correctItem
+     * @param $attributes
+     * @param $answer
+     * @param $listAnswersInfo
+     * @param $displayForStudent
+     * @return string
+     */
+    public static function getFillTheBlankHtml(
+        $separatorStartRegexp,
+        $separatorEndRegexp,
+        $correctItemRegexp,
+        $questionId,
+        $correctItem,
+        $attributes,
+        $answer,
+        $listAnswersInfo,
+        $displayForStudent,
+        $inBlankNumber
+    ) {
+        $result = "";
+        $inTabTeacherSolution = $listAnswersInfo['tabwords'];
+        $inTeacherSolution = $inTabTeacherSolution[$inBlankNumber];
+        switch (self::getFillTheBlankAnswerType($inTeacherSolution)) {
+            case self::FILL_THE_BLANK_MENU:
+                $selected = "";
+                // the blank menu
+                $optionMenu = "";
+                // display a menu from answer separated with |
+                // if display for student, shuffle the correct answer menu
+                $listMenu = self::getFillTheBlankMenuAnswers($inTeacherSolution, $displayForStudent);
+                $result .= '<select name="choice['.$questionId.'][]">';
+                for ($k=0; $k < count($listMenu); $k++) {
+                    $selected = "";
+                    if ($correctItem == $listMenu[$k]) {
+                        $selected = " selected=selected ";
+                    }
+                    // if in teacher view, display the first item by default, which is the right answer
+                    if ($k==0 && !$displayForStudent) {
+                        $selected = " selected=selected ";
+                    }
+                    $optionMenu .= '<option '.$selected.' value="'.$listMenu[$k].'">'.$listMenu[$k].'</option>';
+                }
+                if ($selected == "") {
+                    // no good answer have been found...
+                    $selected = " selected=selected ";
+                }
+                $result .= "<option $selected value=''>--</option>";
+                $result .= $optionMenu;
+                $result .= '</select>';
+                break;
+            case self::FILL_THE_BLANK_SEVERAL_ANSWER:
+                //no break
+            case self::FILL_THE_BLANK_STANDARD:
+            default:
+                $result = Display::input('text', "choice[$questionId][]", $correctItem, $attributes);
+                break;
+        }
+        return $result;
+    }
+
+
+    /**
+     * Return an array with the different choices available when the answers between bracket show as a menu
+     * @param $correctAnswer
+     * @param $displayForStudent  true if we want to shuffle the choices of the menu for students
+     * @return array
+     */
+    public static function getFillTheBlankMenuAnswers($correctAnswer, $displayForStudent)
+    {
+        // if $inDisplayForStudent, then shuffle the result array
+        $listChoises = api_preg_split("/\|/", $correctAnswer);
+        if ($displayForStudent) {
+            shuffle($listChoises);
+        }
+        return $listChoises;
+    }
+
+
+    /**
+     * Return the array index of the student answer
+     * @param $correctAnswer  the menu Choice1|Choice2|Choice3
+     * @param $studentAnswer  the student answer must be Choice1 or Choice2 or Choice3
+     * @return int  in the exemple 0 1 or 2 depending of the choice of the student
+     */
+    public static function getFillTheBlankMenuAnswerNum($correctAnswer, $studentAnswer)
+    {
+        $listChoices = self::getFillTheBlankMenuAnswers($correctAnswer, false);
+        foreach ($listChoices as $num => $value) {
+            if ($value == $studentAnswer) {
+                return $num;
+            }
+        }
+        return -1; // should not happened, because student choose the answer in a menu of possible answers
+    }
+
+
+    /**
+     * Return the possible answer if the answer between brackets is a multiple choice menu
+     * @param $correctAnswer
+     * @return array
+     */
+    public static function getFillTheBlankSeveralAnswers($correctAnswer)
+    {
+        // is answer||Answer||response||Response , mean answer or Answer ...
+        $listSeveral = api_preg_split("/\|\|/", $correctAnswer);
+        return $listSeveral;
+    }
+
+    /**
+     * Return true if student answer is right according to the correctAnswer
+     * it is not as simple as equality, because of the type of Fill The Blank question
+     * eg : studentAnswer = 'Un' and correctAnswer = 'Un||1||un'
+     * @param $studentAnswer the [studentanswer] of the info array of the answer field
+     * @param $correctAnswer the [tabwords] of the info array of the answer field
+     * @return bool
+     */
+    public static function isGoodStudentAnswer($studentAnswer, $correctAnswer)
+    {
+        switch (self::getFillTheBlankAnswerType($correctAnswer)) {
+            case self::FILL_THE_BLANK_MENU:
+                $listMenu = self::getFillTheBlankMenuAnswers($correctAnswer, false);
+                return ($listMenu[0] == $studentAnswer);
+                break;
+            case self::FILL_THE_BLANK_SEVERAL_ANSWER:
+                // the answer must be one of the choice made
+                $listSeveral = self::getFillTheBlankSeveralAnswers($correctAnswer);
+                return (in_array($studentAnswer, $listSeveral));
+                break;
+            case self::FILL_THE_BLANK_STANDARD:
+            default:
+                return ($studentAnswer == $correctAnswer);
+        }
+    }
+
+
+    public static function getFillTheBlankAnswerType($correctAnswer)
+    {
+        if (api_strpos($correctAnswer, "|") && !api_strpos($correctAnswer, "||")) {
+            return self::FILL_THE_BLANK_MENU;
+        } elseif (api_strpos($correctAnswer, "||")) {
+            return self::FILL_THE_BLANK_SEVERAL_ANSWER;
+        } else {
+            return self::FILL_THE_BLANK_STANDARD;
+        }
+    }
+
+    /**
+     * Return information about the answer
+     * @param string $userAnswer : the text of the answer of the question
+     * @param bool $isStudentAnswer : true if it is a student answer and not the empty question model
+     *
+     * @return array of information about the answer
+     */
+    public static function getAnswerInfo($userAnswer = "", $isStudentAnswer = false)
+    {
+        $listAnswerResults = array();
+        $listAnswerResults['text'] = "";
+        $listAnswerResults['wordsCount'] = 0;
+        $listAnswerResults['tabwordsbracket'] = array();
+        $listAnswerResults['tabwords'] = array();
+        $listAnswerResults['tabweighting'] = array();
+        $listAnswerResults['tabinputsize'] = array();
+        $listAnswerResults['switchable'] = "";
+        $listAnswerResults['studentanswer'] = array();
+        $listAnswerResults['studentscore'] = array();
+        $listAnswerResults['blankseparatornumber'] = 0;
+        $listDoubleColon = array();
+
+        api_preg_match("/(.*)::(.*)$/s", $userAnswer, $listResult);
+
+        if (count($listResult) < 2) {
+            $listDoubleColon[] = '';
+            $listDoubleColon[] = '';
+        } else {
+            $listDoubleColon[] = $listResult[1];
+            $listDoubleColon[] = $listResult[2];
+        }
+
+        $listAnswerResults['systemstring'] = $listDoubleColon[1];
+
+        //make sure we only take the last bit to find special marks
+        $listArobaseSplit = explode('@', $listDoubleColon[1]);
+
+        if (count($listArobaseSplit) < 2) {
+            $listArobaseSplit[1] = "";
+        }
+
+        // take the complete string except after the last '::'
+        $listDetails = explode(":", $listArobaseSplit[0]);
+
+        // < number of item after the ::[score]:[size]:[separator_id]@ , here there are 3
+        if (count($listDetails) < 3) {
+            $listWeightings = explode(',', $listDetails[0]);
+            $listSizeOfInput = array();
+            for ($i=0; $i < count($listWeightings); $i++) {
+                $listSizeOfInput[] = 200;
+            }
+            $blankSeparatorNumber = 0;    // 0 is [...]
+        } else {
+            $listWeightings = explode(',', $listDetails[0]);
+            $listSizeOfInput = explode(',', $listDetails[1]);
+            $blankSeparatorNumber = $listDetails[2];
+        }
+
+        $listAnswerResults['text'] = $listDoubleColon[0];
+        $listAnswerResults['tabweighting'] = $listWeightings;
+        $listAnswerResults['tabinputsize'] = $listSizeOfInput;
+        $listAnswerResults['switchable'] = $listArobaseSplit[1];
+        $listAnswerResults['blankseparatorstart'] = self::getStartSeparator($blankSeparatorNumber);
+        $listAnswerResults['blankseparatorend'] = self::getEndSeparator($blankSeparatorNumber);
+        $listAnswerResults['blankseparatornumber'] = $blankSeparatorNumber;
+
+        $blankCharStart = self::getStartSeparator($blankSeparatorNumber);
+        $blankCharEnd = self::getEndSeparator($blankSeparatorNumber);
+        $blankCharStartForRegexp = self::escapeForRegexp($blankCharStart);
+        $blankCharEndForRegexp = self::escapeForRegexp($blankCharEnd);
+
+        // get all blanks words
+        $listAnswerResults['wordsCount'] = api_preg_match_all(
+            '/'.$blankCharStartForRegexp.'[^'.$blankCharEndForRegexp.']*'.$blankCharEndForRegexp.'/',
+            $listDoubleColon[0],
+            $listWords
+        );
+
+        if ($listAnswerResults['wordsCount'] > 0) {
+            $listAnswerResults['tabwordsbracket'] = $listWords[0];
+            // remove [ and ] in string
+            array_walk(
+                $listWords[0],
+                function (&$value, $key, $tabBlankChar) {
+                    $trimChars = "";
+                    for ($i=0; $i < count($tabBlankChar); $i++) {
+                        $trimChars .= $tabBlankChar[$i];
+                    }
+                    $value = trim($value, $trimChars);
+                },
+                array($blankCharStart, $blankCharEnd)
+            );
+            $listAnswerResults['tabwords'] = $listWords[0];
+        }
+
+        // get all common words
+        $commonWords = api_preg_replace(
+            '/'.$blankCharStartForRegexp.'[^'.$blankCharEndForRegexp.']*'.$blankCharEndForRegexp.'/',
+            "::",
+            $listDoubleColon[0]
+        );
+
+        // if student answer, the second [] is the student answer, the third is if student scored or not
+        $listBrackets = array();
+        $listWords =  array();
+        if ($isStudentAnswer) {
+            for ($i=0; $i < count($listAnswerResults['tabwords']); $i++) {
+                $listBrackets[] = $listAnswerResults['tabwordsbracket'][$i];
+                $listWords[] = $listAnswerResults['tabwords'][$i];
+                if ($i+1 < count($listAnswerResults['tabwords'])) {
+                    // should always be
+                    $i++;
+                }
+                $listAnswerResults['studentanswer'][] = $listAnswerResults['tabwords'][$i];
+                if ($i+1 < count($listAnswerResults['tabwords'])) {
+                    // should always be
+                    $i++;
+                }
+                $listAnswerResults['studentscore'][] = $listAnswerResults['tabwords'][$i];
+            }
+            $listAnswerResults['tabwords'] = $listWords;
+            $listAnswerResults['tabwordsbracket'] = $listBrackets;
+
+            // if we are in student view, we've got 3 times :::::: for common words
+            $commonWords = api_preg_replace("/::::::/", "::", $commonWords);
+        }
+        $listAnswerResults['commonwords'] = explode("::", $commonWords);
+
+        return $listAnswerResults;
+    }
+
+
+    /**
+     * Replace the occurence of blank word with [correct answer][student answer][answer is correct]
+     * @param array $listWithStudentAnswer
+     * @return string
+     */
+    public static function getAnswerInStudentAttempt($listWithStudentAnswer)
+    {
+        $separatorStart = $listWithStudentAnswer['blankseparatorstart'];
+        $separatorEnd = $listWithStudentAnswer['blankseparatorend'];
+        // lets rebuild the sentence with [correct answer][student answer][answer is correct]
+        $result = "";
+        for ($i=0; $i < count($listWithStudentAnswer['commonwords']) - 1; $i++) {
+            $result .= $listWithStudentAnswer['commonwords'][$i];
+            $result .= $listWithStudentAnswer['tabwordsbracket'][$i];
+            $result .= $separatorStart.$listWithStudentAnswer['studentanswer'][$i].$separatorEnd;
+            $result .= $separatorStart.$listWithStudentAnswer['studentscore'][$i].$separatorEnd;
+        }
+        $result .= $listWithStudentAnswer['commonwords'][$i];
+        $result .= "::";
+        // add the system string
+        $result .= $listWithStudentAnswer['systemstring'];
+
+        return $result;
+    }
+
+    /**
+     * This function is the same than the js one above getBlankSeparatorRegexp
+     * @param $inChar
+     *
+     * @return string
+     */
+    public static function escapeForRegexp($inChar)
+    {
+        if (in_array($inChar, array(".", "+", "*", "?", "[", "^", "]", "$", "(", ")", "{", "}", "=", "!", ">", "|", ":", "-", ")"))) {
+            return "\\".$inChar;
+        } else {
+            return $inChar;
+        }
+    }
+
+    /**
+     * return $text protected for use in regexp
+     * @param $text
+     *
+     * @return mixed
+     */
+    public static function getRegexpProtected($text)
+    {
+        $listRegexpCharacters = array("/", ".", "+", "*", "?", "[", "^", "]", "$", "(", ")", "{", "}", "=", "!", ">", "|", ":", "-", ")");
+        $result = $text;
+        for ($i=0; $i < count($listRegexpCharacters); $i++) {
+            $result = str_replace($listRegexpCharacters[$i], "\\".$listRegexpCharacters[$i], $result);
+        }
+
+        return $result;
+    }
+
+
+    /**
+     * This function must be the same than the js one getSeparatorFromNumber above
+     * @return array
+     */
+    public static function getAllowedSeparator()
+    {
+        $fillBlanksAllowedSeparator = array(
+            array('[', ']'),
+            array('{', '}'),
+            array('(', ')'),
+            array('*', '*'),
+            array('#', '#'),
+            array('%', '%'),
+            array('$', '$'),
+        );
+
+        return $fillBlanksAllowedSeparator;
+    }
+
+    /**
+     * return the start separator for answer
+     * @param $number
+     * @return mixed
+     */
+    public static function getStartSeparator($number)
+    {
+        $listSeparators = self::getAllowedSeparator();
+
+        return $listSeparators[$number][0];
+    }
+
+    /**
+     * return the end separator for answer
+     * @param $number
+     * @return mixed
+     */
+    public static function getEndSeparator($number)
+    {
+        $listSeparators = self::getAllowedSeparator();
+
+        return $listSeparators[$number][1];
+    }
+
+    /**
+     * Return as a description text, array of allowed separators for question
+     * eg: array("[...]", "(...)")
+     * @return array
+     */
+    public static function getAllowedSeparatorForSelect()
+    {
+        $listResults = array();
+        $fillBlanksAllowedSeparator = self::getAllowedSeparator();
+        for ($i=0; $i < count($fillBlanksAllowedSeparator); $i++) {
+            $listResults[] = $fillBlanksAllowedSeparator[$i][0]."...".$fillBlanksAllowedSeparator[$i][1];
+        }
+
+        return $listResults;
+    }
+
+    /**
+     * return the code number of the separator for the question
+     * @param string $startSeparator
+     * @param string $endSeparator
+     *
+     * @return int
+     */
+    public function getDefaultSeparatorNumber($startSeparator, $endSeparator)
+    {
+        $listSeparators = self::getAllowedSeparator();
+        $result = 0;
+        for ($i=0; $i < count($listSeparators); $i++) {
+            if ($listSeparators[$i][0] == $startSeparator &&
+                $listSeparators[$i][1] == $endSeparator
+            ) {
+                $result = $i;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * return the HTML display of the answer
+     * @param string $answer
+     * @param bool   $resultsDisabled
+     *
+     * @return string
+     */
+    public static function getHtmlDisplayForAnswer($answer, $resultsDisabled = false)
+    {
+        $result = "";
+        $listStudentAnswerInfo = self::getAnswerInfo($answer, true);
+
+        // rebuild the answer with good HTML style
+        // this is the student answer, right or wrong
+        for ($i=0; $i < count($listStudentAnswerInfo['studentanswer']); $i++) {
+            if ($listStudentAnswerInfo['studentscore'][$i] == 1) {
+                $listStudentAnswerInfo['studentanswer'][$i] = self::getHtmlRightAnswer(
+                    $listStudentAnswerInfo['studentanswer'][$i],
+                    $listStudentAnswerInfo['tabwords'][$i],
+                    $resultsDisabled
+                );
+            } else {
+                $listStudentAnswerInfo['studentanswer'][$i] = self::getHtmlWrongAnswer(
+                    $listStudentAnswerInfo['studentanswer'][$i],
+                    $listStudentAnswerInfo['tabwords'][$i],
+                    $resultsDisabled
+                );
+            }
+        }
+
+
+        // rebuild the sentence with student answer inserted
+        for ($i=0; $i < count($listStudentAnswerInfo['commonwords']); $i++) {
+            $result .= isset($listStudentAnswerInfo['commonwords'][$i]) ? $listStudentAnswerInfo['commonwords'][$i] : '';
+            $result .= isset($listStudentAnswerInfo['studentanswer'][$i]) ? $listStudentAnswerInfo['studentanswer'][$i] : '';
+        }
+
+        // the last common word (should be </p>)
+        $result .= isset($listStudentAnswerInfo['commonwords'][$i]) ? $listStudentAnswerInfo['commonwords'][$i] : '';
+
+        return $result;
+    }
+
+    /**
+     * return the HTML code of answer for correct and wrong answer
+     * @param string $answer
+     * @param string $correct
+     * @param string $right
+     * @param bool   $resultsDisabled
+     *
+     * @return string
+     */
+    public static function getHtmlAnswer($answer, $correct, $right, $resultsDisabled = false)
+    {
+        $style = "color: green";
+        if (!$right) {
+            $style = "color: red; text-decoration: line-through;";
+        }
+        $type = FillBlanks::getFillTheBlankAnswerType($correct);
+        switch ($type) {
+            case self::FILL_THE_BLANK_MENU:
+                $correctAnswerHtml = "";
+                $listPossibleAnswers = FillBlanks::getFillTheBlankMenuAnswers($correct, false);
+                $correctAnswerHtml .= "<span style='color: green'>".$listPossibleAnswers[0]."</span>";
+                $correctAnswerHtml .= " <span style='font-weight:normal'>(";
+                for ($i=1; $i < count($listPossibleAnswers); $i++) {
+                    $correctAnswerHtml .= $listPossibleAnswers[$i];
+                    if ($i != count($listPossibleAnswers) - 1) {
+                        $correctAnswerHtml .= " | ";
+                    }
+                }
+                $correctAnswerHtml .= ")</span>";
+                break;
+            case self::FILL_THE_BLANK_SEVERAL_ANSWER:
+                $listCorrects = explode("||", $correct);
+                $firstCorrect = $correct;
+                if (count($listCorrects) > 0) {
+                    $firstCorrect = $listCorrects[0];
+                }
+                $correctAnswerHtml = "<span style='color: green'>".$firstCorrect."</span>";
+                break;
+            case self::FILL_THE_BLANK_STANDARD:
+            default:
+                $correctAnswerHtml = "<span style='color: green'>".$correct."</span>";
+        }
+
+        if ($resultsDisabled) {
+            $correctAnswerHtml = "<span title='".get_lang("ExerciseWithFeedbackWithoutCorrectionComment")."'> - </span>";
+        }
+
+        $result = "<span style='border:1px solid black; border-radius:5px; padding:2px; font-weight:bold;'>";
+        $result .= "<span style='$style'>".$answer."</span>";
+        $result .= "&nbsp;<span style='font-size:120%;'>/</span>&nbsp;";
+        $result .= $correctAnswerHtml;
+        $result .= "</span>";
+
+        return $result;
+    }
+
+    /**
+     * return HTML code for correct answer
+     * @param string $answer
+     * @param string $correct
+     * @param bool   $resultsDisabled
+     *
+     * @return string
+     */
+    public static function getHtmlRightAnswer($answer, $correct, $resultsDisabled = false)
+    {
+        return self::getHtmlAnswer($answer, $correct, true, $resultsDisabled);
+    }
+
+    /**
+     * return HTML code for wrong answer
+     * @param string $answer
+     * @param string $correct
+     * @param bool   $resultsDisabled
+     *
+     * @return string
+     */
+    public static function getHtmlWrongAnswer($answer, $correct, $resultsDisabled = false)
+    {
+        return self::getHtmlAnswer($answer, $correct, false, $resultsDisabled);
+    }
 }

+ 20 - 24
main/exercice/question.class.php

@@ -380,10 +380,7 @@ abstract class Question
                 $res = Database::query($sql);
                 $row = Database::fetch_array($res);
                 if ($row['nb'] > 0) {
-                    //DO nothing
-                    //$sql = "UPDATE $TBL_QUESTION_REL_CATEGORY SET category_id = $category_id
-                    //WHERE question_id=$question_id AND c_id=".api_get_course_int_id();
-                    //$res = Database::query($sql);
+                    // DO nothing
                 } else {
                     $sql = "INSERT INTO $TBL_QUESTION_REL_CATEGORY (c_id, question_id, category_id)
                             VALUES (" . api_get_course_int_id() . ", $question_id, $category_id)";
@@ -775,18 +772,23 @@ abstract class Question
 
         // question already exists
         if(!empty($id)) {
-            $sql = "UPDATE $TBL_QUESTIONS
-                    SET
-                        question = '" . Database::escape_string($question) . "',
-                        description = '" . Database::escape_string($description) . "',
-                        ponderation = '" . Database::escape_string($weighting) . "',
-                        position = '" . Database::escape_string($position) . "',
-                        type = '" . Database::escape_string($type) . "',
-                        picture = '" . Database::escape_string($picture) . "',
-                        extra = '" . Database::escape_string($extra) . "',
-                        level = '" . Database::escape_string($level) . "'
-                    WHERE c_id = $c_id  AND id = " . intval($id);
-            Database::query($sql);
+
+            $params = [
+                'question' => $question,
+                'description' => $description,
+                'ponderation' => $weighting,
+                'position' => $position,
+                'type' => $type,
+                'picture' => $picture,
+                'extra' => $extra,
+                'level' => $level,
+            ];
+
+            Database::update(
+                $TBL_QUESTIONS,
+                $params,
+                ['c_id = ? AND id = ?' => [$c_id, $id]]
+            );
             $this->saveCategory($category);
 
             if (!empty($exerciseId)) {
@@ -834,6 +836,7 @@ abstract class Question
                 'extra' => $extra,
                 'level' => $level
             ];
+
             $this->id = Database::insert($TBL_QUESTIONS, $params);
 
             if ($this->id) {
@@ -910,13 +913,6 @@ abstract class Question
 
         // if the question is created in an exercise
         if ($exerciseId) {
-            /*
-            $sql = 'UPDATE '.Database::get_course_table(TABLE_LP_ITEM).'
-                    SET max_score = '.intval($weighting).'
-                    WHERE item_type = "'.TOOL_QUIZ.'"
-                    AND path='.intval($exerciseId);
-            Database::query($sql);
-            */
             // adds the exercise into the exercise list of this question
             $this->addToList($exerciseId, TRUE);
         }
@@ -1711,7 +1707,7 @@ abstract class Question
 
         $header .= Display::page_subheader2($counter_label . ". " . $question_title);
         $header .= Display::div(
-            "<div class=\"rib rib-$class\"><h3>$score_label'</h3></div> <h4>{$score['result']}</h4>",
+            "<div class=\"rib rib-$class\"><h3>$score_label</h3></div> <h4>{$score['result']}</h4>",
             array('class' => 'ribbon')
         );
         $header .= Display::div($this->description, array('id' => 'question_description'));

+ 2 - 1
main/exercice/stats.php

@@ -52,7 +52,8 @@ if (!empty($question_list)) {
             $question_id,
             $exercise_id,
             $courseCode,
-            $sessionId
+            $sessionId,
+            $question_obj->type
         );
 
         $data[$question_id]['name'] = cut($question_obj->question, 100);

+ 1 - 2
main/forum/forumfunction.inc.php

@@ -722,13 +722,12 @@ function store_forum($values, $courseInfo = array(), $returnId = false)
         $sql_image = '';
         if ($image_moved) {
             $new_file_name = isset($new_file_name) ? $new_file_name : '';
-            $sql_image = "'".$new_file_name."', ";
         }
 
         $params = [
             'c_id' => $course_id,
             'forum_title'=> $values['forum_title'],
-            'forum_image'=> $sql_image,
+            'forum_image'=> $new_file_name,
             'forum_comment'=> isset($values['forum_comment']) ? $values['forum_comment'] : null,
             'forum_category'=> isset($values['forum_category']) ? $values['forum_category'] : null,
             'allow_anonymous'=> isset($values['allow_anonymous_group']['allow_anonymous']) ? $values['allow_anonymous_group']['allow_anonymous'] : null,

+ 0 - 17
main/inc/ajax/course_home.ajax.php

@@ -49,23 +49,6 @@ switch ($action) {
 
             // HIDE AND REACTIVATE TOOL
             if ($_GET["id"] == strval(intval($_GET["id"]))) {
-
-                /* -- session condition for visibility
-                 if (!empty($session_id)) {
-                    $sql = "select session_id FROM $tool_table WHERE id='".$_GET["id"]."' AND session_id = '$session_id'";
-                    $rs = Database::query($sql);
-                    if (Database::num_rows($rs) > 0) {
-                         $sql="UPDATE $tool_table SET visibility=$requested_visible WHERE id='".$_GET["id"]."' AND session_id = '$session_id'";
-                    } else {
-                        $sql_select = "select * FROM $tool_table WHERE id='".$_GET["id"]."'";
-                        $res_select = Database::query($sql_select);
-                        $row_select = Database::fetch_array($res_select);
-                        $sql = "INSERT INTO $tool_table(name,link,image,visibility,admin,address,added_tool,target,category,session_id)
-                                VALUES('{$row_select['name']}','{$row_select['link']}','{$row_select['image']}','0','{$row_select['admin']}','{$row_select['address']}','{$row_select['added_tool']}','{$row_select['target']}','{$row_select['category']}','$session_id')";
-                    }
-                } else $sql="UPDATE $tool_table SET visibility=$requested_visible WHERE id='".$_GET["id"]."'";
-                */
-
                 $sql = "UPDATE $tool_table SET
                         visibility = $requested_visible
                         WHERE c_id = $course_id AND id='" . intval($_GET['id']) . "'";

+ 0 - 4
main/inc/lib/SequenceManager.php

@@ -1017,10 +1017,6 @@ class SequenceManager
 
         if ($row_entity_id_prev !== 0 || $row_entity_id_next !== 0) {
             $seq_table = Database::get_main_table(TABLE_MAIN_SEQUENCE);
-            // Old code
-
-            /*$sql = "UPDATE $seq_table SET sequence_row_entity_id = $row_entity_id_prev WHERE sequence_row_entity_id_next = $row_entity_id_next";
-            Database::query($sql);*/
 
             // Check if exists.
             $sql = "SELECT count(*) as count FROM $seq_table

+ 2 - 34
main/inc/lib/add_course.lib.inc.php

@@ -1087,44 +1087,12 @@ class AddCourse
                 'calification_notebook_title' => '',
                 'numeric_calification' => '',
                 'weight_calification' => '',
-                'forum_category' => $forumCategoryId
+                'forum_category' => $forumCategoryId,
+                'thread_peer_qualify' => 0,
             ];
 
             store_thread($forumInfo, $params, $courseInfo, false);
 
-
-            /*Database::query(
-                "INSERT INTO $TABLEFORUMS (c_id, forum_title, forum_comment, forum_threads,forum_posts,forum_last_post,forum_category, allow_anonymous, allow_edit,allow_attachments, allow_new_threads,default_view,forum_of_group,forum_group_public_private, forum_order,locked,session_id )
-                 VALUES ($course_id, '" . self::lang2db(
-                    get_lang('ExampleForum')
-                ) . "', '', 0, 0, 0, 1, 0, 1, '0', 1, 'flat','0', 'public', 1, 0,0)"
-            );
-            $insert_id = Database:: insert_id();
-            Database::query(
-                "INSERT INTO $TABLEITEMPROPERTY  (c_id, tool,insert_user_id,insert_date,lastedit_date,ref,lastedit_type,lastedit_user_id,to_group_id,to_user_id,visibility)
-                             VALUES ($course_id, '" . TOOL_FORUM . "', 1,'$now','$now',$insert_id,'ForumAdded',1,0,NULL,1)"
-            );*/
-/*
-            Database::query(
-                "INSERT INTO $TABLEFORUMTHREADS (c_id, thread_id, thread_title, forum_id, thread_replies, thread_poster_id, thread_poster_name, thread_views, thread_last_post, thread_date, locked, thread_qualify_max, session_id)
-                            VALUES ($course_id, 1, '" . self::lang2db(
-                    get_lang('ExampleThread')
-                ) . "', 1, 0, 1, '', 0, 1, '$now', 0, 10, 0)"
-            );
-            $insert_id = Database:: insert_id();
-            Database::query(
-                "INSERT INTO $TABLEITEMPROPERTY  (c_id, tool,insert_user_id,insert_date,lastedit_date,ref,lastedit_type,lastedit_user_id,to_group_id,to_user_id,visibility)
-                            VALUES ($course_id, 'forum_thread',1,'$now','$now',$insert_id,'ForumThreadAdded',1,0,NULL,1)"
-            );
-
-            Database::query(
-                "INSERT INTO $TABLEFORUMPOSTS VALUES ($course_id, 1, '" . self::lang2db(
-                    get_lang('ExampleThread')
-                ) . "', '" . self::lang2db(
-                    get_lang('ExampleThreadContent')
-                ) . "', 1, 1, 1, '', '$now', 0, 0, 1)"
-            );*/
-
             /* Gradebook tool */
             $course_code = $courseInfo['code'];
             // father gradebook

+ 3 - 3
main/inc/lib/api.lib.php

@@ -3018,15 +3018,15 @@ function api_display_tool_view_option() {
             // We have to remove the isStudentView=true from the $sourceurl
             $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
             $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
-            $output_string .= '<a class="btn btn-success btn-xs" href="'.$sourceurl.'&isStudentView=false" target="_self">'.get_lang('CourseManagerview').'</a>';
+            $output_string .= '<a class="btn btn-success btn-xs" href="'.$sourceurl.'&isStudentView=false" target="_self">'.get_lang('SwitchToTeacherView').'</a>';
         } elseif ($_SESSION['studentview'] == 'teacherview') {
             // Switching to teacherview
             $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
             $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
-            $output_string .= '<a class="btn btn-primary btn-xs" href="'.$sourceurl.'&isStudentView=true" target="_self">'.get_lang('StudentView').'</a>';
+            $output_string .= '<a class="btn btn-primary btn-xs" href="'.$sourceurl.'&isStudentView=true" target="_self">'.get_lang('SwitchToStudentView').'</a>';
         }
     } else {
-        $output_string .= '<a class="btn btn-primary btn-xs" href="'.$sourceurl.'&isStudentView=true" target="_self">'.get_lang('StudentView').'</a>';
+        $output_string .= '<a class="btn btn-primary btn-xs" href="'.$sourceurl.'&isStudentView=true" target="_self">'.get_lang('SwitchToStudentView').'</a>';
     }
     return $output_string;
 }

+ 76 - 150
main/inc/lib/exercise.lib.php

@@ -548,170 +548,73 @@ HTML;
                     $s .= '</tr>';
 
                 } elseif ($answerType == FILL_IN_BLANKS) {
-                    /*
-                     * In the FILL_IN_BLANKS test
-                     * you mustn't have [ and ] in the textarea
-                     * you mustn't have :: in the textarea
-                     * the text to find mustn't be empty or contains only spaces
-                     * the text to find mustn't contains HTML tags
-                     * the text to find mustn't contains char "
-                     */
+                    // display the question, with field empty, for student to fill it,
+                    // or filled to display the answer in the Question preview of the exercice/admin.php page
+                    $displayForStudent = true;
+                    $listAnswerInformations = FillBlanks::getAnswerInfo($answer);
+                    $separatorStartRegexp = FillBlanks::escapeForRegexp($listAnswerInformations['blankseparatorstart']);
+                    $separatorEndRegexp = FillBlanks::escapeForRegexp($listAnswerInformations['blankseparatorend']);
 
                     list($answer) = explode('::', $answer);
 
-                    // $correct_answer_list array of array with correct anwsers 0=> [0=>[\p] 1=>[plop]]
-                    api_preg_match_all(
-                        '/\[[^]]+\]/',
-                        $answer,
-                        $correct_answer_list
-                    );
+                    //Correct answers
+                    $correctAnswerList = $listAnswerInformations['tabwords'];
 
-                    // get student answer to display it if student go back to previous fillBlank answer question in a test
+                    //Student's answer
+                    $studentAnswerList = array();
                     if (isset($user_choice[0]['answer'])) {
-                        api_preg_match_all(
-                            '/\[[^]]+\]/',
-                            $user_choice[0]['answer'],
-                            $student_answer_list
-                        );
-                        $student_answer_list_tobecleaned = $student_answer_list[0];
-                        $student_answer_list = array();
-                        // here we got the student answer in a test
-                        // let's clean up the results
-                        /*
-                        Array
-                        (
-                            [0] => Array
-                            (
-                                [0] => [<font color="red"><s>yer</s></font> / <font color="green"><b>ici</b></font>]
-                                [1] => [<font color="red"><s>plop</s></font> / <font color="green"><b>/p</b></font>]
-                            )
-                        )
-                        */
-                        for ($i = 0; $i < count(
-                            $student_answer_list_tobecleaned
-                        ); $i++) {
-                            $answer_corrected = $student_answer_list_tobecleaned[$i];
-                            /*
-                             * we got if student answer is wrong
-                             * [<font color="red"><s>rrr</s></font> / <font color="green"><b>/p</b></font>]
-                             * or if student answer is good
-                             * [plop / <font color="green"><b>plop</b></font>]
-                             * or if student didn't answer []
-                             */
-                            $answer_corrected = api_preg_replace(
-                                '| / <font color="green"><b>.*$|',
-                                '',
-                                $answer_corrected
-                            );
-                            /*
-                             * we got [<font color="red"><s>rrr</s></font> or [plop or [
-                             */
-                            $answer_corrected = api_preg_replace(
-                                '/^\[/',
-                                '',
-                                $answer_corrected
-                            );
-                            /*
-                             * we got <font color="red"><s>rrr</s></font> or plop
-                             * non breakable spaces &nbsp;&nbsp;&nbsp; from /main/exercice/exercise.class.php have been removed l 2391 and l 2370
-                             */
-                            $answer_corrected = api_preg_replace(
-                                '|^<font color="red"><s>|',
-                                '',
-                                $answer_corrected
-                            );
-                            $answer_corrected = api_preg_replace(
-                                '|</s></font>$|',
-                                '',
-                                $answer_corrected
-                            );
-                            $answer_corrected = '[' . $answer_corrected . ']';
-                            /*
-                             * we got [rrr] or [plop] or []
-                             */
-                            $student_answer_list[] = $answer_corrected;
-                        }
+                        $arrayStudentAnswer = FillBlanks::getAnswerInfo($user_choice[0]['answer'], true);
+                        $studentAnswerList = $arrayStudentAnswer['studentanswer'];
                     }
 
-                    // If display preview of answer in test view for exemple, set the student answer to the correct answers
+                    // If the question must be shown with the answer (in page exercice/admin.php) for teacher preview
+                    // set the student-answer to the correct answer
                     if ($debug_mark_answer) {
-                        // contain the rights answers surronded with brackets
-                        $student_answer_list = $correct_answer_list[0];
+                        $studentAnswerList = $correctAnswerList;
+                        $displayForStudent = false;
                     }
 
-                    /*
-                    Split the response by bracket
-                    tab_comments is an array with text surrounding the text to find
-                    we add a space before and after the answer_question to be sure to
-                    have a block of text before and after [xxx] patterns
-                    so we have n text to find ([xxx]) and n+1 block of texts before,
-                    between and after the text to find
-                    */
-                    $tab_comments = api_preg_split(
-                        '/\[[^]]+\]/',
-                        ' ' . $answer . ' '
-                    );
-
-                    if (!empty($correct_answer_list) && !empty($student_answer_list)) {
+                    if (!empty($correctAnswerList) && !empty($studentAnswerList)) {
                         $answer = "";
-
-                        $i = 0;
-                        foreach ($student_answer_list as $student_item) {
-                            // remove surronding brackets
-                            $student_response = api_substr(
-                                $student_item,
-                                1,
-                                api_strlen($student_item) - 2
-                            );
-
-                            $size = strlen($student_item);
-                            $attributes['class'] = self::detectInputAppropriateClass(
-                                $size
-                            );
-
-                            $answer .= $tab_comments[$i] .
-                                Display::input(
-                                    'text',
-                                    "choice[$questionId][]",
-                                    $student_response,
-                                    $attributes
-                                );
-                            $i++;
+                        for ($i = 0; $i < count($listAnswerInformations["commonwords"]) - 1; $i++) {
+                            // display the common word
+                            $answer .= $listAnswerInformations["commonwords"][$i];
+                            // display the blank word
+                            $correctItem = $listAnswerInformations["tabwords"][$i];
+                            $correctItemRegexp = $correctItem;
+                            // replace / with \/ to allow the preg_replace bellow and all the regexp char
+                            $correctItemRegexp = FillBlanks::getRegexpProtected($correctItemRegexp);
+                            if (isset($studentAnswerList[$i])) {
+                                // If student already started this test and answered this question,
+                                // fill the blank with his previous answers
+                                // may be "" if student viewed the question, but did not fill the blanks
+                                $correctItem = $studentAnswerList[$i];
+                            }
+                            $attributes["style"] = "width:".$listAnswerInformations["tabinputsize"][$i]."px";
+                            $answer .= FillBlanks::getFillTheBlankHtml($separatorStartRegexp, $separatorEndRegexp, $correctItemRegexp, $questionId, $correctItem, $attributes, $answer, $listAnswerInformations, $displayForStudent, $i);
                         }
-                        $answer .= $tab_comments[$i];
+                        // display the last common word
+                        $answer .= $listAnswerInformations["commonwords"][$i];
                     } else {
-                        // display exercise with empty input fields
-                        // every [xxx] are replaced with an empty input field
-                        foreach ($correct_answer_list[0] as $item) {
-                            $size = strlen($item);
-                            $attributes['class'] = self::detectInputAppropriateClass(
-                                $size
-                            );
-
-                            $attributes['class'] .= ' block_on_enter';
-
-                            $answer = str_replace(
-                                $item,
-                                Display::input(
-                                    'text',
-                                    "choice[$questionId][]",
-                                    '',
-                                    $attributes
-                                ),
-                                $answer
-                            );
+                        // display empty [input] with the right width for student to fill it
+                        $separatorStartRegexp = FillBlanks::escapeForRegexp($listAnswerInformations['blankseparatorstart']);
+                        $separatorEndRegexp = FillBlanks::escapeForRegexp($listAnswerInformations['blankseparatorend']);
+                        $answer = "";
+                        for ($i = 0; $i < count($listAnswerInformations["commonwords"]) - 1; $i++) {
+                            // display the common words
+                            $answer .= $listAnswerInformations["commonwords"][$i];
+                            // display the blank word
+                            $attributes["style"] = "width:".$listAnswerInformations["tabinputsize"][$i]."px";
+                            $correctItem = $listAnswerInformations["tabwords"][$i];
+                            $correctItemRegexp = $correctItem;
+                            // replace / with \/ to allow the preg_replace bellow and all the regexp char
+                            $correctItemRegexp = FillBlanks::getRegexpProtected($correctItemRegexp);
+                            $answer .= FillBlanks::getFillTheBlankHtml($separatorStartRegexp, $separatorEndRegexp, $correctItemRegexp, $questionId, '', $attributes, $answer, $listAnswerInformations, $displayForStudent, $i);
                         }
-                        /*$answer = api_preg_replace(
-                            '/\[[^]]+\]/',
-                            Display::input(
-                                'text',
-                                "choice[$questionId][]",
-                                '',
-                                $attributes
-                            ), $answer);*/
+                        // display the last common word
+                        $answer .= $listAnswerInformations["commonwords"][$i];
                     }
-                    $html = '<div class="fill_blanks col-md-12">'.$answer.'</div>';
-                    $s .= $html ;
+                    $s .= $answer;
 
                 } elseif ($answerType == CALCULATED_ANSWER) {
                     /*
@@ -3047,13 +2950,15 @@ HTML;
      * @param int $exercise_id
      * @param string $course_code
      * @param int $session_id
+     * @param string $questionType
      * @return int
      */
     public static function get_number_students_question_with_answer_count(
         $question_id,
         $exercise_id,
         $course_code,
-        $session_id
+        $session_id,
+        $questionType = ''
     ) {
         $track_exercises = Database::get_main_table(
             TABLE_STATISTIC_TRACK_E_EXERCISES
@@ -3072,6 +2977,27 @@ HTML;
         $courseId = api_get_course_int_id($course_code);
         $session_id = intval($session_id);
 
+        if ($questionType == FILL_IN_BLANKS) {
+            $listStudentsId = array();
+            $listAllStudentInfo = CourseManager::get_student_list_from_course_code(
+                api_get_course_id(),
+                true
+            );
+            foreach ($listAllStudentInfo as $i => $listStudentInfo) {
+                $listStudentsId[] = $listStudentInfo['user_id'];
+            }
+
+            $listFillTheBlankResult = getFillTheBlankTabResult(
+                $exercise_id,
+                $question_id,
+                $listStudentsId,
+                '1970-01-01',
+                '3000-01-01'
+            );
+
+            return getNbResultFillBlankAll($listFillTheBlankResult);
+        }
+
         if (empty($session_id)) {
             $courseCondition = "
             INNER JOIN $courseUser cu

+ 20 - 17
main/inc/lib/exercise_show_functions.lib.php

@@ -22,31 +22,33 @@ class ExerciseShowFunctions
 	 * @param string    Answer text
 	 * @param int       Exercise ID
 	 * @param int       Question ID
+     * @param int $resultsDisabled
 	 * @return void
 	 */
-	static function display_fill_in_blanks_answer($feedback_type, $answer, $id, $questionId)
+	public static function display_fill_in_blanks_answer($feedbackType, $answer, $id, $questionId, $resultsDisabled)
     {
+        $answerHTML = FillBlanks::getHtmlDisplayForAnswer($answer, $resultsDisabled);
         if (empty($id)) {
-            echo '<tr><td>'. Security::remove_XSS($answer).'</td></tr>';
+            echo '<tr><td>';
+            echo Security::remove_XSS($answerHTML, COURSEMANAGERLOWSECURITY);
+            echo '</td></tr>';
         } else {
-		?>
-			<tr>
+            ?>
+            <tr>
                 <td>
-                    <?php
-                    echo Security::remove_XSS($answer);
-                    ?>
+                    <?php echo nl2br(Security::remove_XSS($answerHTML, COURSEMANAGERLOWSECURITY)); ?>
                 </td>
 
-			<?php
-			if (!api_is_allowed_to_edit(null,true) && $feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) { ?>
-				<td>
-                    <?php
-                    $comm = Event::get_comments($id,$questionId);
-                    ?>
-				</td>
-			<?php } ?>
+                <?php
+                if (!api_is_allowed_to_edit(null,true) && $feedbackType != EXERCISE_FEEDBACK_TYPE_EXAM) { ?>
+                    <td>
+                        <?php
+                        $comm = Event::get_comments($id, $questionId);
+                        ?>
+                    </td>
+                <?php } ?>
             </tr>
-		<?php
+        <?php
         }
 	}
 
@@ -152,7 +154,8 @@ class ExerciseShowFunctions
 	 * @param string $studentChoice
 	 * @param string $answerComment
 	 */
-	static function display_hotspot_answer($feedback_type, $answerId, $answer, $studentChoice, $answerComment, $in_results_disabled) {
+	static function display_hotspot_answer($feedback_type, $answerId, $answer, $studentChoice, $answerComment, $in_results_disabled)
+	 {
         $hide_expected_answer = false;
         if ($feedback_type == 0 && $in_results_disabled == 2) {
             $hide_expected_answer = true;

+ 4 - 4
main/inc/lib/export.lib.inc.php

@@ -46,12 +46,12 @@ class Export
         $writer = new CsvWriter();
         $writer->setStream(fopen($filePath, 'w'));
 
-        foreach($data as $item) {
+        foreach ($data as $item) {
             $writer->writeItem($item);
         }
         $writer->finish();
 
-        DocumentManager::file_send_for_download($filePath, false, $filename.'.csv');
+        DocumentManager::file_send_for_download($filePath, true, $filename.'.csv');
         exit;
 	}
 
@@ -74,7 +74,7 @@ class Export
 
         $writer->finish();
 
-        DocumentManager::file_send_for_download($filePath, false, $filename.'.xls');
+        DocumentManager::file_send_for_download($filePath, true, $filename.'.xls');
         exit;
 	}
 
@@ -104,7 +104,7 @@ class Export
         }
         fwrite($handle, '</table></body></html>');
         fclose($handle);
-        DocumentManager::file_send_for_download($file, false, $filename.'.xls');
+        DocumentManager::file_send_for_download($file, true, $filename.'.xls');
     }
 
     /**

+ 61 - 0
main/inc/lib/extra_field.lib.php

@@ -61,6 +61,8 @@ class ExtraField extends Model
     const FIELD_TYPE_FLOAT = 17;
     const FIELD_TYPE_FILE = 18;
     const FIELD_TYPE_VIDEO_URL = 19;
+    const FIELD_TYPE_LETTERS_ONLY = 20;
+    const FIELD_TYPE_ALPHANUMERIC = 21;
 
     public $type = 'user';
     public $pageName;
@@ -335,6 +337,8 @@ class ExtraField extends Model
         $types[self::FIELD_TYPE_FLOAT] = get_lang('FieldTypeFloat');
         $types[self::FIELD_TYPE_FILE] = get_lang('FieldTypeFile');
         $types[self::FIELD_TYPE_VIDEO_URL] = get_lang('FieldTypeVideoUrl');
+        $types[self::FIELD_TYPE_LETTERS_ONLY] = get_lang('FieldTypeOnlyLetters');
+        $types[self::FIELD_TYPE_ALPHANUMERIC] = get_lang('FieldTypeAlphanumeric');
 
         switch ($handler) {
             case 'course':
@@ -1442,6 +1446,63 @@ EOF;
                             ['placeholder' => 'https://']
                         );
                         break;
+                    case ExtraField::FIELD_TYPE_LETTERS_ONLY:
+                        $form->addElement(
+                            'text',
+                            'extra_' . $field_details['variable'],
+                            $field_details['display_text'],
+                            [
+                                'pattern' => '[a-zA-Z]+',
+                                'title' => get_lang('OnlyLetters')
+                            ]
+                        );
+                        $form->applyFilter('extra_' . $field_details['variable'], 'stripslashes');
+                        $form->applyFilter('extra_' . $field_details['variable'], 'trim');
+                        $form->addRule(
+                            'extra_' . $field_details['variable'],
+                            get_lang('OnlyLetters'),
+                            'lettersonly'
+                        );
+
+                        if (!$admin_permissions) {
+                            if ($field_details['visible'] == 0) {
+                                $form->freeze(
+                                    'extra_' . $field_details['variable']
+                                );
+                            }
+                        }
+                        break;
+                    case ExtraField::FIELD_TYPE_ALPHANUMERIC:
+                        $form->addElement(
+                            'text',
+                            'extra_' . $field_details['variable'],
+                            $field_details['display_text'],
+                            [
+                                'pattern' => '[a-zA-Z0-9]+',
+                                'title' => get_lang('OnlyLettersAndNumbers')
+                            ]
+                        );
+                        $form->applyFilter(
+                            'extra_' . $field_details['variable'],
+                            'stripslashes'
+                        );
+                        $form->applyFilter(
+                            'extra_' . $field_details['variable'],
+                            'trim'
+                        );
+                        $form->addRule(
+                            'extra_' . $field_details['variable'],
+                            get_lang('OnlyLettersAndNumbers'),
+                            'alphanumeric'
+                        );
+                        if (!$admin_permissions) {
+                            if ($field_details['visible'] == 0) {
+                                $form->freeze(
+                                    'extra_' . $field_details['variable']
+                                );
+                            }
+                        }
+                        break;
                 }
             }
         }

+ 1 - 1
main/inc/lib/formvalidator/Element/Number.php

@@ -24,6 +24,6 @@ class Number extends HTML_QuickForm_text
 
         parent::__construct($elementName, $elementLabel, $attributes);
         $this->_appendName = true;
-        $this->_type = 'number';
+        $this->setType('number');
     }
 }

+ 33 - 18
main/inc/lib/notebook.lib.php

@@ -118,32 +118,48 @@ class NotebookManager
      * @author Patrick Cool <patrick.cool@ugent.be>, Ghent University, Belgium
      * @version januari 2009, dokeos 1.8.6
      */
-    static function update_note($values) {
+    static function update_note($values)
+    {
         if (!is_array($values) or empty($values['note_title'])) {
             return false;
         }
         // Database table definition
-        $t_notebook = Database :: get_course_table(TABLE_NOTEBOOK);
+        $table = Database :: get_course_table(TABLE_NOTEBOOK);
 
         $course_id = api_get_course_int_id();
         $sessionId = api_get_session_id();
 
-        $sql = "UPDATE $t_notebook SET
-					user_id = '" . api_get_user_id() . "',
-					course = '" . Database::escape_string(api_get_course_id()) . "',
-					session_id = '" . $sessionId . "',
-					title = '" . Database::escape_string($values['note_title']) . "',
-					description = '" . Database::escape_string($values['note_comment']) . "',
-					update_date = '" . Database::escape_string(date('Y-m-d H:i:s')) . "'
-				WHERE c_id = $course_id AND notebook_id = '" . Database::escape_string($values['notebook_id']) . "'";
-        $result = Database::query($sql);
-        $affected_rows = Database::affected_rows($result);
+        $params = [
+            'user_id' => api_get_user_id(),
+            'course' => api_get_course_id(),
+            'session_id' => $sessionId,
+            'title' => $values['note_title'],
+            'description' => $values['note_comment'],
+            'update_date' => api_get_utc_datetime(),
+        ];
+
+        Database::update(
+            $table,
+            $params,
+            [
+                'c_id = ? AND notebook_id = ?' => [
+                    $course_id,
+                    $values['notebook_id'],
+                ],
+            ]
+        );
+
+        // update item_property (update)
+        api_item_property_update(
+            api_get_course_info(),
+            TOOL_NOTEBOOK,
+            $values['notebook_id'],
+            'NotebookUpdated',
+            api_get_user_id()
+        );
+
+        return true;
 
-        //update item_property (update)
-        api_item_property_update(api_get_course_info(), TOOL_NOTEBOOK, $values['notebook_id'], 'NotebookUpdated', api_get_user_id());
-        if (!empty($affected_rows)) {
-            return true;
-        }
     }
 
     static function delete_note($notebook_id)
@@ -170,7 +186,6 @@ class NotebookManager
 
     static function display_notes()
     {
-
         global $_user;
         if (!isset($_GET['direction'])) {
             $sort_direction = 'ASC';

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

@@ -6507,7 +6507,7 @@ class SessionManager
         $form->addElement('html', '<div id="duration" style="display:none">');
 
         $form->addElement(
-            'text',
+            'number',
             'duration',
             array(
                 get_lang('SessionDurationTitle'),

+ 78 - 73
main/lang/english/trad4all.inc.php

@@ -325,18 +325,18 @@ $DeleteUsersNotInList = "Unsubscribe students which are not in the imported list
 $IfSessionExistsUpdate = "If a session exists, update it";
 $CreatedByXYOnZ = "Create by <a href=\"%s\">%s</a> on %s";
 $LoginWithExternalAccount = "Login without an institutional account";
-$ImportAikenQuizExplanationExample = "This is the text for question 1
-A. Answer 1
-B. Answer 2
-C. Answer 3
-ANSWER: B
-
-This is the text for question 2
-A. Answer 1
-B. Answer 2
-C. Answer 3
-D. Answer 4
-ANSWER: D
+$ImportAikenQuizExplanationExample = "This is the text for question 1
+A. Answer 1
+B. Answer 2
+C. Answer 3
+ANSWER: B
+
+This is the text for question 2
+A. Answer 1
+B. Answer 2
+C. Answer 3
+D. Answer 4
+ANSWER: D
 ANSWER_EXPLANATION: this is an optional feedback comment that will appear next to the correct answer.";
 $ImportAikenQuizExplanation = "The Aiken format comes in a simple text (.txt) file, with several question blocks, each separated by a blank line. The first line is the question, the answer lines are prefixed by a letter and a dot, and the correct answer comes next with the ANSWER: prefix. See example below.";
 $ExerciseAikenErrorNoAnswerOptionGiven = "The imported file has at least one question without any answer (or the answers do not include the required prefix letter). Please make sure each question has at least one answer and that it is prefixed by a letter and a dot or a parenthesis, like this: A. answer one";
@@ -425,18 +425,18 @@ $VersionUpToDate = "Your version is up-to-date";
 $LatestVersionIs = "The latest version is";
 $YourVersionNotUpToDate = "Your version is not up-to-date";
 $Hotpotatoes = "Hotpotatoes";
-$ZeroMeansNoQuestionWillBeSelectedMinusOneMeansThatAllQuestionsWillBeSelected = "-1 = All questions will be selected.
+$ZeroMeansNoQuestionWillBeSelectedMinusOneMeansThatAllQuestionsWillBeSelected = "-1 = All questions will be selected.
  0 = No questions will be selected.";
-$EmailNotificationTemplateDescription = "You can customize the email sent to users when they finished the exercise. You can use tags like these:
-
-1. {{ student.username }}
-2. {{ student.firstname }}
-3. {{ student.lastname }}
-4. {{ student.official_code }}
-5. {{ exercise.title }}
-6. {{ exercise.start_time }}
-7. {{ exercise.end_time }}
-8. {{ course.title }}
+$EmailNotificationTemplateDescription = "You can customize the email sent to users when they finished the exercise. You can use tags like these:
+
+1. {{ student.username }}
+2. {{ student.firstname }}
+3. {{ student.lastname }}
+4. {{ student.official_code }}
+5. {{ exercise.title }}
+6. {{ exercise.start_time }}
+7. {{ exercise.end_time }}
+8. {{ course.title }}
 9. {{ course.code }}";
 $EmailNotificationTemplate = "Email notification template";
 $ExerciseEndButtonDisconnect = "Logout";
@@ -844,10 +844,10 @@ $AllowVisitors = "Allow visitors";
 $EnableIframeInclusionComment = "Allowing arbitrary iframes in the HTML Editor will enhance the edition capabilities of the users, but it can represent a security risk. Please make sure you can rely on your users (i.e. you know who they are) before enabling this feature.";
 $AddedToLPCannotBeAccessed = "This exercise has been included in a learning path, so it cannot be accessed by students directly from here. If you want to put the same exercise available through the exercises tool, please make a copy of the current exercise using the copy icon.";
 $EnableIframeInclusionTitle = "Allow iframes in HTML Editor";
-$MailTemplateRegistrationMessage = "Dear ((firstname)) ((lastname)),\n\nYou are registered on
-((sitename)) with the following settings:\n\nUsername :
-((username))\nPass : ((password))\n\nThe address of ((sitename)) is :
-((url))\n\nIn case of trouble, contact us.\n\nYours sincerely
+$MailTemplateRegistrationMessage = "Dear ((firstname)) ((lastname)),\n\nYou are registered on
+((sitename)) with the following settings:\n\nUsername :
+((username))\nPass : ((password))\n\nThe address of ((sitename)) is :
+((url))\n\nIn case of trouble, contact us.\n\nYours sincerely
 \n((admin_name)) ((admin_surname)).";
 $Explanation = "Once you click on \"Create a course\", a course is created with a section for Tests, Project based learning, Assessments, Courses, Dropbox, Agenda and much more. Logging in as teacher provides you with editing privileges for this course.";
 $CodeTaken = "This training code is already in use.  <br>Use the <b>Back</b> button on your browser and try again";
@@ -2640,16 +2640,16 @@ $NoPosts = "No posts";
 $WithoutAchievedSkills = "Without achieved skills";
 $TypeMessage = "Please type your message!";
 $ConfirmReset = "Do you really want to delete all messages?";
-$MailCronCourseExpirationReminderBody = "Dear %s,
-
-It has come to our attention that you have not completed the course %s although its expiration date had been set on %s, remaining %s days to finish it.
-
-We remind you that you have only the possibility to follow this course once a year, that is why we invite you insistently to complete your course on time.
-
-You can return to the course connecting to the platform through this address: %s
-
-Best Regards,
-
+$MailCronCourseExpirationReminderBody = "Dear %s,
+
+It has come to our attention that you have not completed the course %s although its expiration date had been set on %s, remaining %s days to finish it.
+
+We remind you that you have only the possibility to follow this course once a year, that is why we invite you insistently to complete your course on time.
+
+You can return to the course connecting to the platform through this address: %s
+
+Best Regards,
+
 %s Team";
 $MailCronCourseExpirationReminderSubject = "Urgent: %s course expiration reminder";
 $ExerciseAndLearningPath = "Exercise and learning path";
@@ -3105,8 +3105,8 @@ $MyStats = "View my progress";
 $Logout = "Logout";
 $MyAgenda = "Personal agenda";
 $CourseHomepage = "Course home";
-$CourseManagerview = "Teacher View";
-$StudentView = "Learner View";
+$SwitchToTeacherView = "Switch to teacher view";
+$SwitchToStudentView = "Switch to student view";
 $AddResource = "Add it";
 $AddedResources = "Attachments";
 $TimeReportForTeacherX = "Time report for teacher %s";
@@ -5778,8 +5778,8 @@ $CheckThatYouHaveEnoughQuestionsInYourCategories = "Make sure you have enough qu
 $PortalCoursesLimitReached = "Sorry, this installation has a courses limit, which has now been reached. To increase the number of courses allowed on this Chamilo installation, please contact your hosting provider or, if available, upgrade to a superior hosting plan.";
 $PortalTeachersLimitReached = "Sorry, this installation has a teachers limit, which has now been reached. To increase the number of teachers allowed on this Chamilo installation, please contact your hosting provider or, if available, upgrade to a superior hosting plan.";
 $PortalUsersLimitReached = "Sorry, this installation has a users limit, which has now been reached. To increase the number of users allowed on this Chamilo installation, please contact your hosting provider or, if available, upgrade to a superior hosting plan.";
-$GenerateSurveyAccessLinkExplanation = "By copying the link below and pasting it in an e-mail or on a website, you will allow any anonymous person to enter and answer this survey.
-You can test this feature by clicking the link above and answering the survey.
+$GenerateSurveyAccessLinkExplanation = "By copying the link below and pasting it in an e-mail or on a website, you will allow any anonymous person to enter and answer this survey.
+You can test this feature by clicking the link above and answering the survey.
 This is particularly useful if you want to allow anyone on a forum to answer you survey and you don't know their e-mail addresses.";
 $LinkOpenSelf = "Open self";
 $LinkOpenBlank = "Open blank";
@@ -5832,8 +5832,8 @@ $Item = "Item";
 $ConfigureDashboardPlugin = "Configure Dashboard Plugin";
 $EditBlocks = "Edit blocks";
 $Never = "Never";
-$YourAccountIsActiveYouCanLoginAndCheckYourCourses = "Dear user, 
-
+$YourAccountIsActiveYouCanLoginAndCheckYourCourses = "Dear user, 
+
 Your account has now been activated on the platform. Please login and enjoy your courses.";
 $SessionFields = "Session fields";
 $CopyLabelSuffix = "Copy";
@@ -5895,7 +5895,7 @@ $CourseSettingsRegisterDirectLink = "If your course is public or open, you can u
 $DirectLink = "Direct link";
 $here = "here";
 $GoAheadAndBrowseOurCourseCatalogXOnceRegisteredYouWillSeeTheCourseHereX = "<p>Go ahead and browse our course catalog %s to register to any course you like. Once registered, you will see the course appear right %s, instead of this message.</p>";
-$HelloXAsYouCanSeeYourCourseListIsEmpty = "<p>Hello <strong>%s</strong> and welcome,</p>
+$HelloXAsYouCanSeeYourCourseListIsEmpty = "<p>Hello <strong>%s</strong> and welcome,</p>
 <p>As you can see, your courses list is still empty. That's because you are not registered to any course yet! </p>";
 $UnsubscribeUsersAlreadyAddedInCourse = "Unsubscribe users already added";
 $ImportUsers = "Import users";
@@ -6159,7 +6159,7 @@ $AverageScore = "Average score";
 $LastConnexionDate = "Last connexion date";
 $ToolVideoconference = "Videoconference";
 $BigBlueButtonEnableTitle = "BigBlueButton videoconference tool";
-$BigBlueButtonEnableComment = "Choose whether you want to enable the BigBlueButton videoconference tool. Once enabled, it will show as an additional course tool in all courses' homepage, and teachers will be able to launch a conference at any time. Learners will not be able to launch a conference, only join one. If you don't have a BigBlueButton server, please <a href=\"http://bigbluebutton.org/\" target=\"_blank\">set one up</a> or ask the <a href=\"http://www.chamilo.org/en/providers\" target=\"_blank\">Chamilo official providers</a> for a quote.
+$BigBlueButtonEnableComment = "Choose whether you want to enable the BigBlueButton videoconference tool. Once enabled, it will show as an additional course tool in all courses' homepage, and teachers will be able to launch a conference at any time. Learners will not be able to launch a conference, only join one. If you don't have a BigBlueButton server, please <a href=\"http://bigbluebutton.org/\" target=\"_blank\">set one up</a> or ask the <a href=\"http://www.chamilo.org/en/providers\" target=\"_blank\">Chamilo official providers</a> for a quote.
 BigBlueButton is a free (as in freedom *and* beer), but its installation requires a set of technical skills that might not be immediately available to all. You can install it on your own or seek professional help to assist you or do it for you. This help, however, will generate a certain cost. In the pure logic of the free software, we offer you the tools to make your work easier and recommend professionals (the Chamilo Official Providers) that will be able to help you if this were too difficult.";
 $BigBlueButtonHostTitle = "BigBlueButton server host";
 $BigBlueButtonHostComment = "This is the name of the server where your BigBlueButton server is running. Might be <i>localhost</i>, an IP address (e.g. 192.168.13.54) or a domain name (e.g. my.video.com).";
@@ -6170,14 +6170,14 @@ $OnlyAccessFromYourGroup = "Only accessible from your group";
 $CreateAssignmentPage = "This will create a special wiki page in which the teacher can describe the task and which will be automatically linked to the wiki pages where learners perform the task. Both the teacher's and the learners' pages are created automatically. In these tasks, learners can only edit and view theirs pages, but this can be changed easily if you need to.";
 $UserFolders = "Folders of users";
 $UserFolder = "User folder";
-$HelpUsersFolder = "INFORMATION VISIBLE TO THE TEACHER ONLY:\nThe users folder contains a folder for each user who has accessed it through the documents tool, or when any file has been sent in the course through the online editor. If neither circumstances has occurred, then no user folder will have been created. In the case of groups, files that are sent through the editor will be added in the folder of each group, which is only accessible by students from this group.
-<br /><br />
-The users folder and each of the included folders will be hidden by default in for all students, but each student can see the contents of his/her directory through the online editor. However, if a student knows the address of a file or folder of another student, he may be able to access it.
-<br /><br />
-If the folder of a student is visible, other students can see what it contains. In this case, the student that owns the folder can also (from the documents tool and only in his/her folder): create and edit web documents, convert a document into a template for personal use, create and edit drawings in SVG and PNG formats, record audio files in WAV format, make audio files in MP3 from a text, make snapshops from a webcam, send documents, create folders, move folders and files, delete folders and files, and download backup of his/her folder.
-<br /><br />
-Moreover, the documents tool is synchronized with the file manager of the online editor, so changes in the documents triggered in any one of these will affect both.
-<br /><br />
+$HelpUsersFolder = "INFORMATION VISIBLE TO THE TEACHER ONLY:\nThe users folder contains a folder for each user who has accessed it through the documents tool, or when any file has been sent in the course through the online editor. If neither circumstances has occurred, then no user folder will have been created. In the case of groups, files that are sent through the editor will be added in the folder of each group, which is only accessible by students from this group.
+<br /><br />
+The users folder and each of the included folders will be hidden by default in for all students, but each student can see the contents of his/her directory through the online editor. However, if a student knows the address of a file or folder of another student, he may be able to access it.
+<br /><br />
+If the folder of a student is visible, other students can see what it contains. In this case, the student that owns the folder can also (from the documents tool and only in his/her folder): create and edit web documents, convert a document into a template for personal use, create and edit drawings in SVG and PNG formats, record audio files in WAV format, make audio files in MP3 from a text, make snapshops from a webcam, send documents, create folders, move folders and files, delete folders and files, and download backup of his/her folder.
+<br /><br />
+Moreover, the documents tool is synchronized with the file manager of the online editor, so changes in the documents triggered in any one of these will affect both.
+<br /><br />
 As such, the user folder is not only a place to deposit files, it becomes a complete manager of the documents students use during the course. Also, remember that any user can copy a file that is visible from any folder in the documents tool (whether or not he is the owner) to his/her portfolios or personal documents area of social network, which will be available to him/her for use in other courses.";
 $HelpFolderChat = "INFORMATION VISIBLE TO THE TEACHER ONLY:\nThis folder contains all sessions that have been opened in the chat. Although the chat sessions can often be trivial, others can be really interesting and worthy of being incorporated as an additional work document. To do this without changing the visibility of this folder, make the file visible and link it from where you deem appropriate. It is not recommended to make this folder visible to all.";
 $HelpFolderCertificates = "INFORMATION VISIBLE TO THE TEACHER ONLY:\nThis folder contains the various certificates templates that have been created for the rating tool. It is not recommended to make this folder visible to all.";
@@ -6227,8 +6227,8 @@ $Pediaphon = "Use Pediaphon audio services";
 $HelpPediaphon = "Supports text with several thousands characters, in various types of male and female voices (depending on the language). Audio files will be generated and automatically saved to the Chamilo directory in which you are.";
 $FirstSelectALanguage = "Please select a language";
 $MoveUserStats = "Move users results from/to a session";
-$CompareUserResultsBetweenCoursesAndCoursesInASession = "This advanced tool allows you to manually improve the tracking of users results when moving from courses methodology to sessions methodology. In most cases, you won't need to use it.<br />
-On this screen, you can compare results of users between the context of a standalone course, and the context of the same course inside a session.<br />
+$CompareUserResultsBetweenCoursesAndCoursesInASession = "This advanced tool allows you to manually improve the tracking of users results when moving from courses methodology to sessions methodology. In most cases, you won't need to use it.<br />
+On this screen, you can compare results of users between the context of a standalone course, and the context of the same course inside a session.<br />
 Once you are sure about what to do, you can choose to move the tracking data of the students (exercises results and learning paths tracking) from a course to a session.";
 $PDFExportWatermarkEnableTitle = "Enable watermark in PDF export";
 $PDFExportWatermarkEnableComment = "By enabling this option, you can upload an image or a text that will be automatically added as watermark to all PDF exports of documents on the system.";
@@ -6363,8 +6363,8 @@ $MailNotifyInvitation = "Notify by mail on new invitation received";
 $MailNotifyMessage = "Notify by mail on new personal message received";
 $MailNotifyGroupMessage = "Notify by mail on new message received in group";
 $SearchEnabledTitle = "Fulltext search";
-$SearchEnabledComment = "This feature allows you to index most of the documents uploaded to your portal, then provide a search feature for users.<br />
-This feature will not index documents that have already been uploaded, so it is important to enable (if wanted) at the beginning of your implementation.<br />
+$SearchEnabledComment = "This feature allows you to index most of the documents uploaded to your portal, then provide a search feature for users.<br />
+This feature will not index documents that have already been uploaded, so it is important to enable (if wanted) at the beginning of your implementation.<br />
 Once enabled, a search box will appear in the courses list of every user. Searching for a specific term will bring a list of corresponding documents, exercises or forum topics, filtered depending on the availability of these contents to the user.";
 $SpecificSearchFieldsAvailable = "Available custom search fields";
 $XapianModuleInstalled = "Xapian module installed";
@@ -7439,14 +7439,14 @@ $DistinctUsersLogins = "Distinct users logins";
 $AreYouSureToSubscribe = "Are you sure to subscribe?";
 $CheckYourEmailAndFollowInstructions = "Check your email and follow the instructions.";
 $LinkExpired = "Link expired, please try again.";
-$ResetPasswordInstructions = "Change your password instructions";
-$ResetPasswordCommentWithUrl = "You are receiving this notification because you have (or someone pretending to be you) has requested a new password be sent for your account. If you did not request this notification then
-please ignore it, if you keep receiving it please contact the board
-administrator.
-
-To set a the new password you need to activate it. To do this click the link provided below.
-
-%s";
+$ResetPasswordInstructions = "Instructions for the password change procedure";
+$ResetPasswordCommentWithUrl = "You are receiving this message because you (or someone pretending to be you) have requested a new password to be generated for you.
+
+To set a the new password you need to activate it. To do this, please click this link:
+
+%s
+
+If you did not request this procedure, then please ignore this message. If you keep receiving it, please contact the portal administrator.";
 $CronRemindCourseExpirationActivateText = "Remind Course Expiration cron";
 $CronRemindCourseExpirationActivateComment = "Enable the Remind Course Expiration cron";
 $CronRemindCourseExpirationFrecuencyText = "Frecuency for the Remind Course Expiration cron";
@@ -7454,17 +7454,22 @@ $CronRemindCourseExpirationFrecuencyComment = "Number of days before the expirat
 $CronCourseFinishedActivateText = "Course Finished cron";
 $CronCourseFinishedActivateComment = "Activate the Course Finished cron";
 $MailCronCourseFinishedSubject = "End of course %s";
-$MailCronCourseFinishedBody = "Dear %s,
-
-Thanks for participating of the course %s. We hope you've learned and enjoyed the course.
-
-You can check your performance over the course in the My Progress section.
-
-Best Regards,
-
+$MailCronCourseFinishedBody = "Dear %s,
+
+Thank you for your participation to course %s. We hope you've acquired new relevant knowledge and enjoyed the course.
+
+You can check your performance in the course through the My Progress section.
+
+Best regards,
+
 %s Team";
 $GenerateDefaultContent = "Generate default content";
 $ThanksForYourSubscription = "Thanks for your subscription";
 $XTeam = "%s team.";
 $YouCanStartSubscribingToCoursesEnteringToXUrl = "You can start subscribing to courses entering to %s";
+$VideoUrl = "Video URL";
+$AddAttachment = "Add attachment";
+$FieldTypeOnlyLetters = "Text only letters";
+$FieldTypeAlphanumeric = "Text only alphanumeric characters";
+$OnlyLetters = "Only letters";
 ?>

+ 29 - 2
main/lang/french/trad4all.inc.php

@@ -3096,8 +3096,8 @@ $MyStats = "Mon parcours";
 $Logout = "Quitter";
 $MyAgenda = "Agenda perso";
 $CourseHomepage = "Sommaire du cours";
-$CourseManagerview = "Vue prof";
-$StudentView = "Vue apprenant";
+$SwitchToTeacherView = "Passer en vue prof";
+$SwitchToStudentView = "Passer en vue apprenant";
 $AddResource = "Ajouter une ressource";
 $AddedResources = "Ressources ajoutées";
 $TimeReportForTeacherX = "Rapport de temps pour l'enseignant %s";
@@ -7429,4 +7429,31 @@ $SeeSequences = "Voir les séquences";
 $SessionRequirements = "Pré-requis pour la session";
 $IsRequirement = "Obligatoire pour compléter le cours";
 $ConsiderThisGradebookAsRequirementForASessionSequence = "Considérer ce cahier de notes comme un pré-requis pour compléter le cours (ce qui influencera notamment les séquences de sessions)";
+$DistinctUsersLogins = "Connexions d'utilisateurs distincts";
+$AreYouSureToSubscribe = "Êtes-vous certain de vouloir vous inscrire?";
+$CheckYourEmailAndFollowInstructions = "Veuillez vérifier votre e-mail et suivre les instructions.";
+$LinkExpired = "Lien expiré, merci de recommencer la procédure.";
+$ResetPasswordInstructions = "Instructions de changement de mot de passe";
+$ResetPasswordCommentWithUrl = "Ce message vous est envoyé parce que vous (ou quelqu'un qui tente d'usurper votre identité) a demandé qu'un nouveau mot de passe soit généré pour votre compte. Pour configurer votre nouveau mot de passe, veuillez suivre le lien suivant: %s 
+Si vous n'avez rien demandé, vous pouvez ignorer ce message en toute tranquilité. Si vous n'avez rien demandé et que vous continuez de recevoir ces messages, merci de bien vouloir contacter l'administrateur du portail.";
+$CronRemindCourseExpirationActivateText = "Cron de rappel de fin de cours";
+$CronRemindCourseExpirationActivateComment = "Activez cette option pour envoyer un e-mail de rappel quand la fin du cours (session) approche (requiert la configuration d'un processus cron sur le serveur).";
+$CronRemindCourseExpirationFrecuencyText = "Fréquence du rappel de fin de cours";
+$CronRemindCourseExpirationFrecuencyComment = "Quantité de jours avant la fin du cours (session) pour envoyer le rappel par e-mail. Par exemple, indiquez 7 pour envoyer un e-mail 7 jours avant la fin du cours.";
+$CronCourseFinishedActivateText = "Cron de fin du cours";
+$CronCourseFinishedActivateComment = "Activez pour envoyer un e-mail lorsque le cours est terminé";
+$MailCronCourseFinishedSubject = "Fin du cours: %s";
+$MailCronCourseFinishedBody = "Cher/Chère %s,
+
+Merci d'avoir participé au cours %s. Nous espérons que vous avez acquis de nouvelles connaissances et que vous avez pleinement profité du cours. Vous pouvez réviser votre progrès et votre développement personnel au sein du cours dans la section Mon Suivi.
+
+Cordialement,
+
+%s";
+$GenerateDefaultContent = "Générer du contenu par défaut";
+$ThanksForYourSubscription = "Merci pour votre inscription";
+$XTeam = "Équipe %s";
+$YouCanStartSubscribingToCoursesEnteringToXUrl = "Vous pouvez vous inscrire à des cours en allant sur %s";
+$VideoUrl = "URL de vidéo";
+$AddAttachment = "Ajouter une pièce jointe";
 ?>

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 58 - 58
main/lang/spanish/trad4all.inc.php


+ 0 - 16
main/newscorm/learnpath.class.php

@@ -998,22 +998,6 @@ class learnpath
             return false;
         }
         $this->current_time_stop = time();
-        if ($this->save) {
-            /*
-            $learnpath_view_table = Database :: get_course_table(TABLE_LP_VIEW);
-            $sql = "UPDATE $learnpath_view_table " .
-                    "SET " .
-                    "stop_time = ".$this->current_time_stop.", " .
-                    "score = ".$this->current_score.", ".
-                    "WHERE learnpath_id = '".$this->lp_id."'";
-            //$res = Database::query($sql);
-            $res = Database::query($res);
-            if (Database::affected_rows($res) < 1) {
-                $this->error = 'Could not update learnpath_view table while closing learnpath';
-                return false;
-            }
-            */
-        }
         $this->ordered_items = array();
         $this->index = 0;
         unset ($this->lp_id);

+ 41 - 39
main/session/session_course_user.php

@@ -16,16 +16,18 @@ $id_session = intval($_GET['id_session']);
 SessionManager::protectSession($id_session);
 
 // setting breadcrumbs
-//$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='.Security::remove_XSS($_GET['id_session']),'name' => get_lang('SessionOverview'));
+$interbreadcrumb[] = array(
+    'url' => 'resume_session.php?id_session='.$id_session,
+    'name' => get_lang('SessionOverview'),
+);
 
 // Database Table Definitions
-$tbl_session_rel_course_rel_user	= Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
-$tbl_session						= Database::get_main_table(TABLE_MAIN_SESSION);
-$tbl_session_rel_user				= Database::get_main_table(TABLE_MAIN_SESSION_USER);
-$tbl_session_rel_course				= Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
-$tbl_course							= Database::get_main_table(TABLE_MAIN_COURSE);
+$tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
+$tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
+$tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
+$tbl_session_rel_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
+$tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
 
 // setting the name of the tool
 $tool_name = get_lang('EditSessionCoursesByUser');
@@ -47,17 +49,16 @@ if (!api_is_platform_admin()) {
 }
 
 $formSent = 0;
-$errorMsg = $firstLetterCourse = $firstLetterSession = '';
 $CourseList = $SessionList = array();
 $courses = $sessions = array();
 $noPHP_SELF = true;
 
 if (isset($_POST['formSent']) && $_POST['formSent']) {
     $formSent = $_POST['formSent'];
-    $CourseList = $_POST['SessionCoursesList'];
+    $CourseList = isset($_POST['SessionCoursesList']) ? ($_POST['SessionCoursesList']) : null;
 
     if (!is_array($CourseList)) {
-        $CourseList=array();
+        $CourseList = array();
     }
 
     $sql = "SELECT DISTINCT course.id
@@ -72,8 +73,16 @@ if (isset($_POST['formSent']) && $_POST['formSent']) {
 
     $rs = Database::query($sql);
     $existingCourses = Database::store_result($rs);
+
+    if (empty($CourseList) && empty($existingCourses)) {
+        Display::addFlash(Display::return_message(get_lang('NoCoursesForThisSession'), 'warning'));
+        header('Location: session_course_user.php?id_session='.$id_session.'&id_user='.$id_user);
+        exit;
+    }
+
     if (count($CourseList) == count($existingCourses)) {
-        header('Location: session_course_user.php?id_session='.$id_session.'&id_user='.$id_user.'&msg='.get_lang('MaybeYouWantToDeleteThisUserFromSession'));
+        Display::addFlash(Display::return_message(get_lang('MaybeYouWantToDeleteThisUserFromSession')));
+        header('Location: session_course_user.php?id_session='.$id_session.'&id_user='.$id_user);
         exit;
     }
 
@@ -85,16 +94,17 @@ if (isset($_POST['formSent']) && $_POST['formSent']) {
             }
         }
 
-        if(!$exists) {
+        if (!$exists) {
             $enreg_course = Database::escape_string($enreg_course);
-            $sql_delete = "DELETE FROM $tbl_session_rel_course_rel_user
-						   WHERE user_id = '".$id_user."' AND c_id='".$enreg_course."' AND session_id=$id_session";
-            $result = Database::query($sql_delete);
+            $sql = "DELETE FROM $tbl_session_rel_course_rel_user
+                    WHERE user_id = '".$id_user."' AND c_id='".$enreg_course."' AND session_id=$id_session";
+            $result = Database::query($sql);
             if (Database::affected_rows($result)) {
                 //update session rel course table
-                $sql_update  = "UPDATE $tbl_session_rel_course SET nbr_users= nbr_users - 1
-                                WHERE session_id='$id_session' AND c_id = '$enreg_course'";
-                Database::query($sql_update);
+                $sql = "UPDATE $tbl_session_rel_course
+                        SET nbr_users= nbr_users - 1
+                        WHERE session_id='$id_session' AND c_id = '$enreg_course'";
+                Database::query($sql);
             }
         }
     }
@@ -102,28 +112,26 @@ if (isset($_POST['formSent']) && $_POST['formSent']) {
     foreach ($existingCourses as $existingCourse) {
         if (!in_array($existingCourse['id'], $CourseList)){
             $existingCourse = Database::escape_string($existingCourse['id']);
-            $sql = "INSERT IGNORE INTO $tbl_session_rel_course_rel_user(session_id,c_id,user_id)
+            $sql = "INSERT IGNORE INTO $tbl_session_rel_course_rel_user (session_id,c_id,user_id)
                     VALUES ('$id_session','$existingCourse','$id_user')";
             $result = Database::query($sql);
             if (Database::affected_rows($result)) {
                 //update session rel course table
-                $sql  = "UPDATE $tbl_session_rel_course SET nbr_users= nbr_users + 1
-                         WHERE session_id='$id_session' AND c_id = '$existingCourse'";
+                $sql = "UPDATE $tbl_session_rel_course
+                        SET nbr_users= nbr_users + 1
+                        WHERE session_id='$id_session' AND c_id = '$existingCourse'";
                 Database::query($sql);
             }
         }
     }
-    header('Location: session_course_user.php?id_session='.$id_session.'&id_user='.$id_user.'&msg='.get_lang('CoursesUpdated'));
+    Display::addFlash(Display::return_message(get_lang('CoursesUpdated')));
+
+    header('Location: session_course_user.php?id_session='.$id_session.'&id_user='.$id_user);
     exit;
 }
 
-// display the dokeos header
 Display::display_header($tool_name);
 
-if (!empty($_GET['msg'])) {
-    Display::display_normal_message(urldecode($_GET['msg']));
-}
-
 // the form header
 $session_info = SessionManager::fetch($id_session);
 $user_info = api_get_user_info($id_user);
@@ -142,8 +150,8 @@ $sql_all="SELECT course.id, code, title, visual_code, src.session_id
         FROM $tbl_course course
         INNER JOIN $tbl_session_rel_course  as src
         ON course.id = src.c_id AND session_id = $id_session";
-$result=Database::query($sql);
-$Courses=Database::store_result($result);
+$result = Database::query($sql);
+$Courses = Database::store_result($result);
 
 $result = Database::query($sql_all);
 $CoursesAll = Database::store_result($result);
@@ -153,25 +161,18 @@ foreach ($Courses as $course) {
     $course_temp[] = $course['id'];
 }
 
-foreach($CoursesAll as $course) {
+foreach ($CoursesAll as $course) {
     if (in_array($course['id'], $course_temp)) {
         $nosessionCourses[$course['id']] = $course ;
     } else {
         $sessionCourses[$course['id']] = $course ;
     }
 }
-
 unset($Courses);
 ?>
 
 <form name="formulaire" method="post" action="<?php echo api_get_self(); ?>?id_user=<?php echo $id_user; ?>&id_session=<?php echo $id_session; ?>" style="margin:0px;">
     <input type="hidden" name="formSent" value="1" />
-
-    <?php
-    if(!empty($errorMsg)) {
-        Display::display_normal_message($errorMsg); //main API
-    }
-    ?>
     <table border="0" cellpadding="5" cellspacing="0" width="100%" align="center">
         <tr>
             <td width="45%" align="center"><b><?php echo get_lang('CurrentCourses') ?> :</b></td>
@@ -188,7 +189,7 @@ unset($Courses);
                             <option value="<?php echo $enreg['id']; ?>" <?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></div> <?php
+                        ?> </select></div> <?php
                 unset($nosessionCourses);
                 ?>
 
@@ -203,7 +204,8 @@ unset($Courses);
                 </button>
                 <br /><br /><br /><br /><br /><br />
                 <?php
-                echo '<button class="btn btn-primary" type="button" value="" onclick="valide()" >'.get_lang('EditSessionCourses').'</button>';
+                echo '<button class="btn btn-primary" type="button" value="" onclick="valide()" >'.
+                    get_lang('EditSessionCourses').'</button>';
                 ?>
             </td>
             <td width="45%" align="center">

+ 11 - 13
main/template/default/mail/content_registration_platform.tpl

@@ -1,13 +1,11 @@
-{{ 'Dear'|get_lang }} {{ complete_name }},
-</br>
-{{ 'YouAreReg'|get_lang }} {{ _s.site_name }} {{ 'WithTheFollowingSettings'|get_lang }}</br>
-{{ 'Username'|get_lang }} : {{ login_name }}
-{{ 'Pass'|get_lang }} : {{ original_password }}
-</br></br>
-{{ 'Address'|get_lang }} {{ _s.site_name }} {{ 'Is'|get_lang }} : {{ mailWebPath }}</br>
-{{ 'Problem'|get_lang }}</br>
-{{ 'SignatureFormula'|get_lang }}</br>
-{{ _admin.name }}, {{ _admin.surname }}
-{{ 'Manager'|get_lang }} {{ _s.site_name }}
-{{ _admin.telephone ? 'T. ' ~ _admin.telephone ~ '<br>' }}
-{{ _admin.email ? 'Email'|get_lang ~ ': ' ~ _admin.email }}
+<p>{{ 'Dear'|get_lang }} {{ complete_name }},</p>
+<p>{{ 'YouAreReg'|get_lang }} {{ _s.site_name }} {{ 'WithTheFollowingSettings'|get_lang }}</p>
+<p>{{ 'Username'|get_lang }} : {{ login_name }}<br>
+    {{ 'Pass'|get_lang }} : {{ original_password }}</p>
+<p>{{ 'Address'|get_lang }} {{ _s.site_name }} {{ 'Is'|get_lang }} : {{ mailWebPath }}</p>
+<p>{{ 'Problem'|get_lang }}</p>
+<p>{{ 'SignatureFormula'|get_lang }}</p>
+<p>{{ _admin.name }}, {{ _admin.surname }}<br>
+    {{ 'Manager'|get_lang }} {{ _s.site_name }}<br>
+    {{ _admin.telephone ? 'T. ' ~ _admin.telephone }}<br>
+    {{ _admin.email ? 'Email'|get_lang ~ ': ' ~ _admin.email }}</p>

+ 9 - 12
main/template/default/mail/content_subscription_to_session_confirmation.tpl

@@ -1,12 +1,9 @@
-{{ 'Dear'|get_lang }} {{ complete_name }},
-</br>
-{{ 'YouAreRegisterToSessionX'|get_lang|format(session_name) }}
-</br>
-{{ 'Address'|get_lang }}  {{ _s.site_name }} {{ 'Is'|get_lang }} : {{ _p.web }}
-</br>
-{{ 'Problem'|get_lang }}</br>
-{{ 'SignatureFormula'|get_lang }}</br>
-{{ _admin.name }} {{ _admin.surname }}
-{{ 'Manager'|get_lang }} {{ _s.site_name }}
-{{ _admin.telephone ? 'T. ' ~ _admin.telephone ~ '<br>' }}
-{{ _admin.email ? 'Email'|get_lang ~ ': ' ~ _admin.email }}
+<p>{{ 'Dear'|get_lang }} {{ complete_name }},</p>
+<p>{{ 'YouAreRegisterToSessionX'|get_lang|format(session_name) }}</p>
+<p>{{ 'Address'|get_lang }}  {{ _s.site_name }} {{ 'Is'|get_lang }} : {{ _p.web }}</p>
+<p>{{ 'Problem'|get_lang }}</p>
+<p>{{ 'SignatureFormula'|get_lang }}</p>
+<p>{{ _admin.name }} {{ _admin.surname }}<br>
+    {{ 'Manager'|get_lang }} {{ _s.site_name }}<br>
+    {{ _admin.telephone ? 'T. ' ~ _admin.telephone }}<br>
+    {{ _admin.email ? 'Email'|get_lang ~ ': ' ~ _admin.email }}</p>

+ 1 - 1
main/template/default/mail/cron_course_finished_body.tpl

@@ -1 +1 @@
-{{ 'MailCronCourseFinishedBody'|get_lang|format(complete_user_name, session_name, session_name, _s.site_name) }}
+<p>{{ 'MailCronCourseFinishedBody'|get_lang|format(complete_user_name, session_name, session_name, _s.site_name) }}</p>

+ 1 - 1
main/template/default/mail/cron_remind_course_expiration_body.tpl

@@ -1 +1 @@
-{{ 'MailCronCourseExpirationReminderBody'|get_lang|format(complete_user_name, session_name, session_access_end_date, remaining_days, _p.web, _s.institution) }}
+<p>{{ 'MailCronCourseExpirationReminderBody'|get_lang|format(complete_user_name, session_name, session_access_end_date, remaining_days, _p.web, _s.institution) }}</p>

+ 8 - 6
main/user/subscribe_user.php

@@ -1,10 +1,12 @@
 <?php
 /* For licensing terms, see /license.txt*/
+
 /**
 * This script allows teachers to subscribe existing users
 * to their course.
 * @package chamilo.user
 */
+
 require_once '../inc/global.inc.php';
 $current_course_tool  = TOOL_USER;
 
@@ -152,18 +154,18 @@ if (isset($_POST['action'])) {
             }
 
 			if (!empty($list_register_user)) {
-				if ($is_suscribe_counter==1) {
-					$register_user_message=$temp_unique_user.' '.get_lang('AddedToCourse');
+                if ($is_suscribe_counter == 1) {
+					$register_user_message = $temp_unique_user.' '.get_lang('AddedToCourse');
 					Display::addFlash(Display::return_message($register_user_message));
 				} else {
-					$register_user_message='<br />'.get_lang('UsersRegistered').'<br/><br />'.$list_register_user;
-                    Display::addFlash(Display::return_message($register_user_message, false));
+					$register_user_message = get_lang('UsersRegistered').'<br/><br />'.$list_register_user;
+                    Display::addFlash(Display::return_message($register_user_message, 'normal', false));
 				}
 			}
 
 			if (!empty($list_not_register_user)) {
-				$not_register_user_message='<br />'.get_lang('UsersNotRegistered').'<br/><br /><br />'.$list_not_register_user;
-                Display::addFlash(Display::return_message($not_register_user_message, 'error'));
+				$not_register_user_message = get_lang('UsersNotRegistered').'<br/><br /><br />'.$list_not_register_user;
+                Display::addFlash(Display::return_message($not_register_user_message, 'error', false));
 			}
 
             header('Location:'.api_get_path(WEB_CODE_PATH).'user/user.php?'.api_get_cidreq().'&type='.$type);

+ 3 - 7
main/user/user.php

@@ -52,7 +52,7 @@ if (api_is_allowed_to_edit(null, true)) {
                     $user_ids = array_diff($_POST['user'], array($user_id));
                     if (count($user_ids) > 0) {
                         CourseManager::unsubscribe_user($user_ids, $courseCode);
-                        $message = get_lang('UsersUnsubscribed');
+                        Display::addFlash(Display::return_message(get_lang('UsersUnsubscribed')));
                     }
                 }
         }
@@ -421,9 +421,9 @@ if (api_is_allowed_to_edit(null, true)) {
             $row = Database::fetch_array($result, 'ASSOC');
             if ($row['user_id'] == $user_id || $row['user_id'] == "") {
                 CourseManager::unsubscribe_user($_GET['user_id'], $courseCode);
-                $message = get_lang('UserUnsubscribed');
+                Display::addFlash(Display::return_message(get_lang('UserUnsubscribed')));
             } else {
-                $message = get_lang('ThisStudentIsSubscribeThroughASession');
+                Display::addFlash(Display::return_message(get_lang('ThisStudentIsSubscribeThroughASession')));
             }
         }
     }
@@ -982,10 +982,6 @@ if (api_is_allowed_to_edit(null, true)) {
 
 echo UserManager::getUserSubscriptionTab($selectedTab);
 
-if (isset($message)) {
-    Display::display_confirmation_message($message);
-}
-
 $table->display();
 
 if (!empty($_GET['keyword']) && !empty($_GET['submit'])) {

+ 21 - 0
tests/datafiller/data_courses.php

@@ -19,3 +19,24 @@ $courses[] = array(
     'expiration_date' => '2020-09-01 00:00:00',
     'exemplary_content' => true,
 );
+$courses[] = array(
+    'code' => 'SPANISH101',
+    'title' => 'Español para iniciantes',
+    'description' => 'Curso de español',
+    'category_code' => 'PROJ',
+    'course_language' => 'spanish',
+    'user_id' => 1,
+    'expiration_date' => '2020-09-01 00:00:00',
+    'exemplary_content' => true,
+);
+$courses[] = array(
+    'code' => 'FRENCH101',
+    'title' => 'Français pour débutants',
+    'description' => 'Cours de français',
+    'category_code' => 'PROJ',
+    'course_language' => 'french',
+    'user_id' => 1,
+    'expiration_date' => '2020-09-01 00:00:00',
+    'exemplary_content' => true,
+);
+

+ 4 - 2
tests/main/chat/chat_functions.lib.test.php

@@ -43,9 +43,11 @@ class TestChatFunctions extends UnitTestCase {
 		//The user_id exists so we must do an UPDATE and not a INSERT
 		$current_time = date('Y-m-d H:i:s');
 		if (Database::num_rows($result)==0) {
-			$query="INSERT INTO $tbl_chat_connected(user_id,last_connection,session_id) VALUES(1,'$current_time','$session_id')";
+			$query="INSERT INTO $tbl_chat_connected(user_id,last_connection,session_id)
+					VALUES(1,'$current_time','$session_id')";
 		} else {
-			$query="UPDATE $tbl_chat_connected set last_connection='".$current_time."' WHERE user_id=1 AND session_id='$session_id'";
+			$query="UPDATE $tbl_chat_connected set last_connection='".$current_time."'
+					WHERE user_id=1 AND session_id='$session_id'";
 		}
 		Database::query($query);
 	}

+ 2 - 2
tests/scripts/insert_session_fields.php

@@ -47,7 +47,7 @@ $recommendedNumberOfParticipants->save(array(
 
 $place = new ExtraField('session');
 $place->save(array(
-    'field_type' => ExtraField::FIELD_TYPE_TEXT,
+    'field_type' => ExtraField::FIELD_TYPE_ALPHANUMERIC,
     'variable' => 'place',
     'display_text' => get_lang('Place'),
     'visible' => 1,
@@ -108,7 +108,7 @@ $isOpenSession->save(array(
 
 $duration = new ExtraField('session');
 $duration->save(array(
-    'field_type' => ExtraField::FIELD_TYPE_TEXT,
+    'field_type' => ExtraField::FIELD_TYPE_LETTERS_ONLY,
     'variable' => 'human_text_duration',
     'display_text' => get_lang('DurationInWords'),
     'visible' => 1,

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels