Parcourir la source

Merge branch '1.10.x' into 1.10.x.new_structure

Julio Montoya il y a 10 ans
Parent
commit
a235d18213

+ 169 - 106
main/admin/index.php

@@ -77,7 +77,7 @@ if (!empty($hook)) {
 
 /* Users */
 
-$blocks['users']['icon']  = Display::return_icon('members.gif', get_lang('Users'), array(), ICON_SIZE_SMALL, false);
+$blocks['users']['icon'] = Display::return_icon('members.gif', get_lang('Users'), array(), ICON_SIZE_SMALL, false);
 $blocks['users']['label'] = api_ucfirst(get_lang('Users'));
 $blocks['users']['class'] = 'block-admin-users';
 
@@ -91,43 +91,51 @@ if (api_is_platform_admin()) {
     $blocks['users']['editable'] = true;
 
     $search_form = '
-            <form method="get" class="form-inline" action="user_list.php">
-                <div class="form-group">
+        <form method="get" class="form-inline" action="user_list.php">
+            <div class="form-group">
                 <input class="form-control" type="text" name="keyword" value="">
-                <button class="btn btn-default" type="submit"><i class="fa fa-search"></i> '.get_lang('Search').'</button>
-                </div>
-            </form>';
+                <button class="btn btn-default" type="submit">
+                    <i class="fa fa-search"></i> ' . get_lang('Search') . '
+                </button>
+            </div>
+        </form>';
     $blocks['users']['search_form'] = $search_form;
     $items = array(
-        array('url'=>'user_list.php', 	'label' => get_lang('UserList')),
-        array('url'=>'user_add.php', 	'label' => get_lang('AddUsers')),
-        array('url'=>'user_export.php', 'label' => get_lang('ExportUserListXMLCSV')),
-        array('url'=>'user_import.php', 'label' => get_lang('ImportUserListXMLCSV')),
-        array('url'=>'user_update_import.php', 'label' => get_lang('EditUserListCSV')),
+        array('url' => 'user_list.php', 'label' => get_lang('UserList')),
+        array('url' => 'user_add.php', 'label' => get_lang('AddUsers')),
+        array('url' => 'user_export.php', 'label' => get_lang('ExportUserListXMLCSV')),
+        array('url' => 'user_import.php', 'label' => get_lang('ImportUserListXMLCSV')),
+        array('url' => 'user_update_import.php', 'label' => get_lang('EditUserListCSV')),
     );
-    $items[] = array('url'=>'group_add.php', 	'label' => get_lang('AddGroups'));
-    $items[] = array('url'=>'group_list.php', 	'label' => get_lang('GroupList'));
+    $items[] = array('url' => 'group_add.php', 'label' => get_lang('AddGroups'));
+    $items[] = array('url' => 'group_list.php', 'label' => get_lang('GroupList'));
 
     if (isset($extAuthSource) && isset($extAuthSource['extldap']) && count($extAuthSource['extldap']) > 0) {
-        $items[] = array('url'=>'ldap_users_list.php', 	'label' => get_lang('ImportLDAPUsersIntoPlatform'));
+        $items[] = array('url' => 'ldap_users_list.php', 'label' => get_lang('ImportLDAPUsersIntoPlatform'));
     }
-    $items[] = array('url'=>'extra_fields.php?type=user', 'label' => get_lang('ManageUserFields'));
+    $items[] = array('url' => 'extra_fields.php?type=user', 'label' => get_lang('ManageUserFields'));
 } else {
     $items = array(
-        array('url'=>'user_list.php', 	'label' => get_lang('UserList')),
-        array('url'=>'user_add.php', 	'label' => get_lang('AddUsers')),
-        array('url'=>'user_import.php', 'label' => get_lang('ImportUserListXMLCSV')),
+        array('url' => 'user_list.php', 'label' => get_lang('UserList')),
+        array('url' => 'user_add.php', 'label' => get_lang('AddUsers')),
+        array('url' => 'user_import.php', 'label' => get_lang('ImportUserListXMLCSV')),
     );
 }
 
-$items[] = array('url'=>'usergroups.php', 	'label' => get_lang('Classes'));
+$items[] = array('url' => 'usergroups.php', 'label' => get_lang('Classes'));
 
 $blocks['users']['items'] = $items;
 $blocks['users']['extra'] = null;
 
 if (api_is_platform_admin()) {
     /* Courses */
-    $blocks['courses']['icon']  = Display::return_icon('course.gif', get_lang('Courses'), array(), ICON_SIZE_MEDIUM, false);
+    $blocks['courses']['icon'] = Display::return_icon(
+        'course.gif',
+        get_lang('Courses'),
+        array(),
+        ICON_SIZE_MEDIUM,
+        false
+    );
     $blocks['courses']['label'] = api_ucfirst(get_lang('Courses'));
     $blocks['courses']['class'] = 'block-admin-courses';
     $blocks['courses']['editable'] = true;
@@ -139,46 +147,54 @@ if (api_is_platform_admin()) {
     }
 
     $search_form = ' <form method="get" class="form-inline" action="course_list.php">
-                            <div class="form-group">
-							<input class="form-control" type="text" name="keyword" value="">
-							<button class="btn btn-default" type="submit"><i class="fa fa-search"></i> '.get_lang('Search').'</button>
-							</div>
-	            		</form>';
+            <div class="form-group">
+                <input class="form-control" type="text" name="keyword" value="">
+                <button class="btn btn-default" type="submit">
+                    <i class="fa fa-search"></i> ' . get_lang('Search') . '
+                </button>
+            </div>
+        </form>';
     $blocks['courses']['search_form'] = $search_form;
 
     $items = array();
-    $items[] = array('url'=>'course_list.php', 	'label' => get_lang('CourseList'));
-    $items[] = array('url'=>'course_add.php', 	'label' => get_lang('AddCourse'));
+    $items[] = array('url' => 'course_list.php', 'label' => get_lang('CourseList'));
+    $items[] = array('url' => 'course_add.php', 'label' => get_lang('AddCourse'));
 
     if (api_get_setting('course_validation') == 'true') {
 
-        $items[] = array('url'=>'course_request_review.php', 	'label' => get_lang('ReviewCourseRequests'));
-        $items[] = array('url'=>'course_request_accepted.php', 	'label' => get_lang('AcceptedCourseRequests'));
-        $items[] = array('url'=>'course_request_rejected.php', 	'label' => get_lang('RejectedCourseRequests'));
+        $items[] = array('url' => 'course_request_review.php', 'label' => get_lang('ReviewCourseRequests'));
+        $items[] = array('url' => 'course_request_accepted.php', 'label' => get_lang('AcceptedCourseRequests'));
+        $items[] = array('url' => 'course_request_rejected.php', 'label' => get_lang('RejectedCourseRequests'));
     }
 
-    $items[] = array('url'=>'course_export.php', 			'label' => get_lang('ExportCourses'));
-    $items[] = array('url'=>'course_import.php', 			'label' => get_lang('ImportCourses'));
-    $items[] = array('url'=>'course_category.php', 			'label' => get_lang('AdminCategories'));
-    $items[] = array('url'=>'subscribe_user2course.php', 	'label' => get_lang('AddUsersToACourse'));
-    $items[] = array('url'=>'course_user_import.php', 		'label' => get_lang('ImportUsersToACourse'));
+    $items[] = array('url' => 'course_export.php', 'label' => get_lang('ExportCourses'));
+    $items[] = array('url' => 'course_import.php', 'label' => get_lang('ImportCourses'));
+    $items[] = array('url' => 'course_category.php', 'label' => get_lang('AdminCategories'));
+    $items[] = array('url' => 'subscribe_user2course.php', 'label' => get_lang('AddUsersToACourse'));
+    $items[] = array('url' => 'course_user_import.php', 'label' => get_lang('ImportUsersToACourse'));
     //$items[] = array('url'=>'course_intro_pdf_import.php', 	'label' => get_lang('ImportPDFIntroToCourses'));
 
     if (api_get_setting('gradebook_enable_grade_model') == 'true') {
-        $items[] = array('url'=>'grade_models.php', 'label' => get_lang('GradeModel'));
+        $items[] = array('url' => 'grade_models.php', 'label' => get_lang('GradeModel'));
     }
 
     if (isset($extAuthSource) && isset($extAuthSource['ldap']) && count($extAuthSource['ldap']) > 0) {
-        $items[] = array('url'=>'ldap_import_students.php', 'label' => get_lang('ImportLDAPUsersIntoCourse'));
+        $items[] = array('url' => 'ldap_import_students.php', 'label' => get_lang('ImportLDAPUsersIntoCourse'));
     }
 
-    $items[] = array('url'=>'extra_fields.php?type=course', 'label' => get_lang('ManageCourseFields'));
+    $items[] = array('url' => 'extra_fields.php?type=course', 'label' => get_lang('ManageCourseFields'));
 
     $blocks['courses']['items'] = $items;
     $blocks['courses']['extra'] = null;
 
     /* Platform */
-    $blocks['platform']['icon']  = Display::return_icon('platform.png', get_lang('Platform'), array(), ICON_SIZE_MEDIUM, false);
+    $blocks['platform']['icon'] = Display::return_icon(
+        'platform.png',
+        get_lang('Platform'),
+        array(),
+        ICON_SIZE_MEDIUM,
+        false
+    );
     $blocks['platform']['label'] = api_ucfirst(get_lang('Platform'));
     $blocks['platform']['class'] = 'block-admin-platform';
     $blocks['platform']['editable'] = true;
@@ -190,53 +206,67 @@ if (api_is_platform_admin()) {
     }
 
     $search_form = ' <form method="get" action="settings.php" class="form-inline">
-                            <div class="form-group">
-							<input class="form-control" type="text" name="search_field" value="" >
-                            <input type="hidden" value="search_setting" name="category">
-							<button class="btn btn-default" type="submit"><i class="fa fa-search"></i> '.get_lang('Search').'</button>
-							</div>
-	            		</form>';
-	$blocks['platform']['search_form'] = $search_form;
+            <div class="form-group">
+                <input class="form-control" type="text" name="search_field" value="" >
+                <input type="hidden" value="search_setting" name="category">
+                <button class="btn btn-default" type="submit">
+                    <i class="fa fa-search"></i> ' . get_lang('Search') . '
+                </button>
+            </div>
+        </form>';
+    $blocks['platform']['search_form'] = $search_form;
 
     $items = array();
-    $items[] = array('url'=>'settings.php', 				'label' => get_lang('PlatformConfigSettings'));
-    $items[] = array('url'=>'settings.php?category=Plugins','label' => get_lang('Plugins'));
-    $items[] = array('url'=>'settings.php?category=Regions','label' => get_lang('Regions'));
-    $items[] = array('url'=>'system_announcements.php', 	'label' => get_lang('SystemAnnouncements'));
-    $items[] = array('url'=> api_get_path(WEB_CODE_PATH).'calendar/agenda_js.php?type=admin', 'label' => get_lang('GlobalAgenda'));
-    $items[] = array('url'=>'configure_homepage.php', 		'label' => get_lang('ConfigureHomePage'));
-    $items[] = array('url'=>'configure_inscription.php', 	'label' => get_lang('ConfigureInscription'));
-    $items[] = array('url'=>'statistics/index.php', 		'label' => get_lang('Statistics'));
-    $items[] = array('url'=> api_get_path(WEB_CODE_PATH).'mySpace/company_reports.php',          'label' => get_lang('Reports'));
+    $items[] = array('url' => 'settings.php', 'label' => get_lang('PlatformConfigSettings'));
+    $items[] = array('url' => 'settings.php?category=Plugins', 'label' => get_lang('Plugins'));
+    $items[] = array('url' => 'settings.php?category=Regions', 'label' => get_lang('Regions'));
+    $items[] = array('url' => 'system_announcements.php', 'label' => get_lang('SystemAnnouncements'));
+    $items[] = array(
+        'url' => api_get_path(WEB_CODE_PATH) . 'calendar/agenda_js.php?type=admin',
+        'label' => get_lang('GlobalAgenda')
+    );
+    $items[] = array('url' => 'configure_homepage.php', 'label' => get_lang('ConfigureHomePage'));
+    $items[] = array('url' => 'configure_inscription.php', 'label' => get_lang('ConfigureInscription'));
+    $items[] = array('url' => 'statistics/index.php', 'label' => get_lang('Statistics'));
+    $items[] = array(
+        'url' => api_get_path(WEB_CODE_PATH) . 'mySpace/company_reports.php',
+        'label' => get_lang('Reports')
+    );
     $items[] = array(
-        'url'=> api_get_path(WEB_CODE_PATH) . 'admin/teacher_time_report.php',
+        'url' => api_get_path(WEB_CODE_PATH) . 'admin/teacher_time_report.php',
         'label' => get_lang('TeacherTimeReport')
     );
 
     /* Event settings */
 
     if (api_get_setting('activate_email_template') == 'true') {
-        $items[] = array('url'=>'event_controller.php?action=listing', 		'label' => get_lang('EventMessageManagement'));
+        $items[] = array('url' => 'event_controller.php?action=listing', 'label' => get_lang('EventMessageManagement'));
     }
 
     if (!empty($_configuration['multiple_access_urls'])) {
         if (api_is_global_platform_admin()) {
-            $items[] = array('url'=>'access_urls.php', 	'label' => get_lang('ConfigureMultipleAccessURLs'));
+            $items[] = array('url' => 'access_urls.php', 'label' => get_lang('ConfigureMultipleAccessURLs'));
         }
     }
 
     if (api_get_setting('allow_reservation') == 'true') {
-        $items[] = array('url'=>'../reservation/m_category.php', 	'label' => get_lang('BookingSystem'));
+        $items[] = array('url' => '../reservation/m_category.php', 'label' => get_lang('BookingSystem'));
     }
     if (api_get_setting('allow_terms_conditions') == 'true') {
-        $items[] = array('url'=>'legal_add.php', 	'label' => get_lang('TermsAndConditions'));
+        $items[] = array('url' => 'legal_add.php', 'label' => get_lang('TermsAndConditions'));
     }
     $blocks['platform']['items'] = $items;
     $blocks['platform']['extra'] = null;
 }
 
 /* Sessions */
-$blocks['sessions']['icon']  = Display::return_icon('session.png', get_lang('Sessions'), array(), ICON_SIZE_SMALL, false);
+$blocks['sessions']['icon'] = Display::return_icon(
+    'session.png',
+    get_lang('Sessions'),
+    array(),
+    ICON_SIZE_SMALL,
+    false
+);
 $blocks['sessions']['label'] = api_ucfirst(get_lang('Sessions'));
 $blocks['sessions']['class'] = 'block-admin-sessions';
 
@@ -252,53 +282,70 @@ if (api_is_platform_admin()) {
 
 $search_form = ' <form method="GET" class="form-inline" action="session_list.php">
                     <div class="form-group">
-                    <input class="form-control" type="text" name="keyword" value="">
-                    <button class="btn btn-default" type="submit"><i class="fa fa-search"></i> '.get_lang('Search').'</button>
+                        <input class="form-control" type="text" name="keyword" value="">
+                        <button class="btn btn-default" type="submit">
+                            <i class="fa fa-search"></i> ' . get_lang('Search') . '
+                        </button>
                     </div>
                 </form>';
 $blocks['sessions']['search_form'] = $search_form;
 $items = array();
-$items[] = array('url'=>'session_list.php', 	'label' => get_lang('ListSession'));
-$items[] = array('url'=>'session_add.php', 	'label' => get_lang('AddSession'));
-$items[] = array('url'=>'session_category_list.php', 	'label' => get_lang('ListSessionCategory'));
-$items[] = array('url'=>'session_import.php', 	'label' => get_lang('ImportSessionListXMLCSV'));
-$items[] = array('url'=>'session_import_drh.php', 	'label' => get_lang('ImportSessionDrhList'));
+$items[] = array('url' => 'session_list.php', 'label' => get_lang('ListSession'));
+$items[] = array('url' => 'session_add.php', 'label' => get_lang('AddSession'));
+$items[] = array('url' => 'session_category_list.php', 'label' => get_lang('ListSessionCategory'));
+$items[] = array('url' => 'session_import.php', 'label' => get_lang('ImportSessionListXMLCSV'));
+$items[] = array('url' => 'session_import_drh.php', 'label' => get_lang('ImportSessionDrhList'));
 if (isset($extAuthSource) && isset($extAuthSource['ldap']) && count($extAuthSource['ldap']) > 0) {
-    $items[] = array('url'=>'ldap_import_students_to_session.php', 	'label' => get_lang('ImportLDAPUsersIntoSession'));
+    $items[] = array(
+        'url' => 'ldap_import_students_to_session.php',
+        'label' => get_lang('ImportLDAPUsersIntoSession')
+    );
 }
-$items[] = array('url'=>'session_export.php', 	'label' => get_lang('ExportSessionListXMLCSV'));
-$items[] = array('url'=>'../coursecopy/copy_course_session.php', 	'label' => get_lang('CopyFromCourseInSessionToAnotherSession'));
+$items[] = array('url' => 'session_export.php', 'label' => get_lang('ExportSessionListXMLCSV'));
+$items[] = array(
+    'url' => '../coursecopy/copy_course_session.php',
+    'label' => get_lang('CopyFromCourseInSessionToAnotherSession')
+);
 
 if (api_is_platform_admin()) {
-    if (is_dir(api_get_path(SYS_TEST_PATH).'datafiller/')) { // option only visible in development mode. Enable through code if required
-        $items[] = array('url'=>'user_move_stats.php', 	'label' => get_lang('MoveUserStats'));
+    // option only visible in development mode. Enable through code if required
+    if (is_dir(api_get_path(SYS_TEST_PATH) . 'datafiller/')) {
+        $items[] = array('url' => 'user_move_stats.php', 'label' => get_lang('MoveUserStats'));
     }
-    $items[] = array('url'=>'career_dashboard.php', 	'label' => get_lang('CareersAndPromotions'));
+    $items[] = array('url' => 'career_dashboard.php', 'label' => get_lang('CareersAndPromotions'));
+    $items[] = array('url' => 'extra_fields.php?type=session', 'label' => get_lang('ManageSessionFields'));
 }
 
-$items[] = array('url'=>'extra_fields.php?type=session', 	'label' => get_lang('ManageSessionFields'));
-
 $blocks['sessions']['items'] = $items;
 $blocks['sessions']['extra'] = null;
 
 /* Settings */
 if (api_is_platform_admin()) {
 
-    $blocks['settings']['icon']  = Display::return_icon('settings.png', get_lang('System'), array(), ICON_SIZE_SMALL, false);
+    $blocks['settings']['icon'] = Display::return_icon(
+        'settings.png',
+        get_lang('System'),
+        array(),
+        ICON_SIZE_SMALL,
+        false
+    );
     $blocks['settings']['label'] = api_ucfirst(get_lang('System'));
     $blocks['settings']['class'] = 'block-admin-settings';
 
 
     $items = array();
-    $items[] = array('url'=>'special_exports.php', 	'label' => get_lang('SpecialExports'));
+    $items[] = array('url' => 'special_exports.php', 'label' => get_lang('SpecialExports'));
     if (!empty($_configuration['db_admin_path'])) {
-        $items[] = array('url'=>$_configuration['db_admin_path'], 	'label' => get_lang('AdminDatabases').' ('.get_lang('DBManagementOnlyForServerAdmin').') ');
+        $items[] = array(
+            'url' => $_configuration['db_admin_path'],
+            'label' => get_lang('AdminDatabases') . ' (' . get_lang('DBManagementOnlyForServerAdmin') . ') '
+        );
     }
-    $items[] = array('url'=>'system_status.php', 	'label' => get_lang('SystemStatus'));
-    if (is_dir(api_get_path(SYS_TEST_PATH).'datafiller/')) {
-        $items[] = array('url'=>'filler.php', 	'label' => get_lang('DataFiller'));
+    $items[] = array('url' => 'system_status.php', 'label' => get_lang('SystemStatus'));
+    if (is_dir(api_get_path(SYS_TEST_PATH) . 'datafiller/')) {
+        $items[] = array('url' => 'filler.php', 'label' => get_lang('DataFiller'));
     }
-    $items[] = array('url'=>'archive_cleanup.php', 	'label' => get_lang('ArchiveDirCleanup'));
+    $items[] = array('url' => 'archive_cleanup.php', 'label' => get_lang('ArchiveDirCleanup'));
 
     if (isset($_configuration['db_manager_enabled']) &&
         $_configuration['db_manager_enabled'] == true &&
@@ -308,7 +355,10 @@ if (api_is_platform_admin()) {
         $username = $_configuration['db_user'];
         $databaseName = $_configuration['main_database'];
 
-        $items[] = array('url'=>"db.php?username=$username&db=$databaseName&server=$host", 'label' => get_lang('Database Manager'));
+        $items[] = array(
+            'url' => "db.php?username=$username&db=$databaseName&server=$host",
+            'label' => get_lang('Database Manager')
+        );
     }
 
     $blocks['settings']['items'] = $items;
@@ -318,20 +368,31 @@ if (api_is_platform_admin()) {
 
     // Skills
     if (api_get_setting('allow_skills_tool') == 'true') {
-        $blocks['skills']['icon']  = Display::return_icon('logo.png', get_lang('Skills'), array(), ICON_SIZE_SMALL, false);
+        $blocks['skills']['icon'] = Display::return_icon(
+            'logo.png',
+            get_lang('Skills'),
+            array(),
+            ICON_SIZE_SMALL,
+            false
+        );
         $blocks['skills']['label'] = get_lang('Skills');
         $blocks['skills']['class'] = 'block-admin-skills';
 
-
         $items = array();
         //$items[] = array('url'=>'skills.php',           'label' => get_lang('SkillsTree'));
-        $items[] = array('url'=>'skills_wheel.php',     'label' => get_lang('SkillsWheel'));
-        $items[] = array('url'=>'skills_import.php',    'label' => get_lang('SkillsImport'));
-        $items[] = array('url'=>'skill_list.php',    'label' => get_lang('ManageSkills'));
+        $items[] = array('url' => 'skills_wheel.php', 'label' => get_lang('SkillsWheel'));
+        $items[] = array('url' => 'skills_import.php', 'label' => get_lang('SkillsImport'));
+        $items[] = array('url' => 'skill_list.php', 'label' => get_lang('ManageSkills'));
         //$items[] = array('url'=>'skills_profile.php',   'label' => get_lang('SkillsProfile'));
-        $items[] = array('url'=>api_get_path(WEB_CODE_PATH).'social/skills_ranking.php',   'label' => get_lang('SkillsRanking'));
-        $items[] = array('url'=>'skills_gradebook.php', 'label' => get_lang('SkillsAndGradebooks'));
-        $items[] = array('url'=> api_get_path(WEB_CODE_PATH) . 'admin/skill_badge.php', 'label' => get_lang('Badges'));
+        $items[] = array(
+            'url' => api_get_path(WEB_CODE_PATH) . 'social/skills_ranking.php',
+            'label' => get_lang('SkillsRanking')
+        );
+        $items[] = array('url' => 'skills_gradebook.php', 'label' => get_lang('SkillsAndGradebooks'));
+        $items[] = array(
+            'url' => api_get_path(WEB_CODE_PATH) . 'admin/skill_badge.php',
+            'label' => get_lang('Badges')
+        );
         $blocks['skills']['items'] = $items;
         $blocks['skills']['extra'] = null;
         $blocks['skills']['search_form'] = null;
@@ -339,28 +400,31 @@ if (api_is_platform_admin()) {
 
     /* Chamilo.org */
 
-    $blocks['chamilo']['icon']  = Display::return_icon('logo.png', 'Chamilo.org', array(), ICON_SIZE_SMALL, false);
+    $blocks['chamilo']['icon'] = Display::return_icon('logo.png', 'Chamilo.org', array(), ICON_SIZE_SMALL, false);
     $blocks['chamilo']['label'] = 'Chamilo.org';
     $blocks['chamilo']['class'] = 'block-admin-chamilo';
 
     $items = array();
-    $items[] = array('url'=>'http://www.chamilo.org/', 	'label' => get_lang('ChamiloHomepage'));
-    $items[] = array('url'=>'http://www.chamilo.org/forum', 	'label' => get_lang('ChamiloForum'));
-
-    $items[] = array('url'=>'../../documentation/installation_guide.html', 	'label' => get_lang('InstallationGuide'));
-    $items[] = array('url'=>'../../documentation/changelog.html', 	'label' => get_lang('ChangesInLastVersion'));
-    $items[] = array('url'=>'../../documentation/credits.html', 	'label' => get_lang('ContributorsList'));
-    $items[] = array('url'=>'../../documentation/security.html', 	'label' => get_lang('SecurityGuide'));
-    $items[] = array('url'=>'../../documentation/optimization.html', 	'label' => get_lang('OptimizationGuide'));
-    $items[] = array('url'=>'http://www.chamilo.org/extensions', 	'label' => get_lang('ChamiloExtensions'));
-    $items[] = array('url'=>'http://www.chamilo.org/en/providers', 	'label' => get_lang('ChamiloOfficialServicesProviders'));
+    $items[] = array('url' => 'http://www.chamilo.org/', 'label' => get_lang('ChamiloHomepage'));
+    $items[] = array('url' => 'http://www.chamilo.org/forum', 'label' => get_lang('ChamiloForum'));
+
+    $items[] = array('url' => '../../documentation/installation_guide.html', 'label' => get_lang('InstallationGuide'));
+    $items[] = array('url' => '../../documentation/changelog.html', 'label' => get_lang('ChangesInLastVersion'));
+    $items[] = array('url' => '../../documentation/credits.html', 'label' => get_lang('ContributorsList'));
+    $items[] = array('url' => '../../documentation/security.html', 'label' => get_lang('SecurityGuide'));
+    $items[] = array('url' => '../../documentation/optimization.html', 'label' => get_lang('OptimizationGuide'));
+    $items[] = array('url' => 'http://www.chamilo.org/extensions', 'label' => get_lang('ChamiloExtensions'));
+    $items[] = array(
+        'url' => 'http://www.chamilo.org/en/providers',
+        'label' => get_lang('ChamiloOfficialServicesProviders')
+    );
 
     $blocks['chamilo']['items'] = $items;
     $blocks['chamilo']['extra'] = null;
     $blocks['chamilo']['search_form'] = null;
 
     //Version check
-    $blocks['version_check']['icon']  = Display::return_icon('logo.png', 'Chamilo.org', array(), ICON_SIZE_SMALL, false);
+    $blocks['version_check']['icon'] = Display::return_icon('logo.png', 'Chamilo.org', array(), ICON_SIZE_SMALL, false);
     $blocks['version_check']['label'] = get_lang('VersionCheck');
     $blocks['version_check']['extra'] = '<div class="admin-block-version"></div>';
     $blocks['version_check']['search_form'] = null;
@@ -378,9 +442,8 @@ if (api_is_platform_admin()) {
             $blocks = $data['blocks'];
         }
     }
-
 }
-$admin_ajax_url = api_get_path(WEB_AJAX_PATH).'admin.ajax.php';
+$admin_ajax_url = api_get_path(WEB_AJAX_PATH) . 'admin.ajax.php';
 
 $tpl = new Template();
 

+ 112 - 5
main/cron/create_course_sessions.php

@@ -2,7 +2,7 @@
 /* For licensing terms, see /license.txt */
 /**
  * Create course sessions procedure. It creates sessions for courses that haven't it yet.
- * If today is greater than OFFSET, it will create them also for the next month.
+ * If today is greater than OFFSET, it will create them also for the next quarter
  * @package chamilo.cron
  * @author Imanol Losada <imanol.losada@beeznest.com>
  */
@@ -32,6 +32,106 @@ function getMonthFirstAndLastDates($initialDate = null)
     return array('startDate' => $startDate, 'endDate' => $endDate);
 }
 
+/**
+ * Same as month, but for quarters
+ * @param   array   $initialDate First day of the quarter
+ * @return  array   First and last days of the quarter
+ */
+function getQuarterFirstAndLastDates($initialDate = null)
+{
+    $startDate = $initialDate ? $initialDate : date("Y-m-01");
+    $month = getQuarterFirstMonth(getQuarter(date('m', $startDate)));
+    $startDate = substr($startDate, 0, 5) . $month . '-01';
+    $nextQuarterStartDate = date('Y-m-d', api_strtotime($startDate.' + 3 month'));
+    $endDate = date('Y-m-d', api_strtotime($nextQuarterStartDate.' - 1 minute'));
+    return array('startDate' => $startDate, 'endDate' => $endDate);
+}
+
+/**
+ * Returns a quarter from a month
+ * @param   string  The month (digit), with or without leading 0
+ * @return  int The yearly quarter (1, 2, 3 or 4) in which this month lies
+ */
+function getQuarter($month)
+{
+    $quarter = 1;
+    // Remove the leading 0 if any
+    if (substr($month, 0, 1) == '0') {
+        $month = substr($month, 1);
+    }
+    // reduce to 4 quarters: 1..3=1; 4..6=2
+    switch ($month) {
+        case 1:
+            //no break
+        case 2:
+            //no break
+        case 3:
+            $quarter = 1;
+            break;
+        case 4:
+            //no break
+        case 5:
+            //no break
+        case 6:
+            $quarter = 2;
+            break;
+        case 7:
+            //no break
+        case 8:
+            //no break
+        case 9:
+            $quarter = 3;
+            break;
+        case 10:
+            //no break
+        case 11:
+            //no break
+        case 12:
+            $quarter = 4;
+            break;
+    }
+    return $quarter;
+}
+
+/**
+ * Returns the first month of the quarter
+ * @param   int Quarter
+ * @return  string Number of the month, with leading 0
+ */
+function getQuarterFirstMonth($quarter)
+{
+    switch ($quarter) {
+        case 1:
+            return '01';
+        case 2:
+            return '04';
+        case 3:
+            return '07';
+        case 4:
+            return '10';
+    }
+    return false;
+}
+
+/**
+ * Get the quarter in Roman letters
+ * @param   int Quarter
+ * @return  string  Roman letters
+ */
+function getQuarterRoman($quarter)
+{
+    switch ($quarter) {
+        case 1:
+            return 'I';
+        case 2:
+            return 'II';
+        case 3:
+            return 'III';
+        case 4:
+            return 'IV';
+    }
+}
+
 /**
  * Creates one session per course with $administratorId as the creator and
  * adds it to the session starting on $startDate and finishing on $endDate
@@ -50,7 +150,13 @@ function createCourseSessions($courses, $administratorId, $startDate, $endDate)
     echo "\n=====================================================================================\n\n";
     // Loop through courses creating one session per each and adding them
     foreach ($courses as $course) {
-        $sessionName = $course['title']." (".date("m/Y", api_strtotime($startDate)).")";
+        //$period = date("m/Y", api_strtotime($startDate));
+        $month = date("m", api_strtotime($startDate));
+        $year = date("Y", api_strtotime($startDate));
+        $quarter = getQuarter($month);
+        $quarter = getQuarterRoman($quarter);
+        $period = $year . '-' . $quarter;
+        $sessionName = '[' . $period . '] ' . $course['title'];
         $sessionId = SessionManager::create_session(
             $sessionName,
             $startDate,
@@ -82,14 +188,15 @@ if (!$lastingAdministrators) {
 $administratorId = intval($administrators[$lastingAdministrators - 1]['user_id']);
 
 // Creates course sessions for the current month
-$dates = getMonthFirstAndLastDates(date('Y-m-').'01');
+$dates = getQuarterFirstAndLastDates(date('Y-m-').'01');
 // Get courses that don't have any session
 $courses = CourseManager::getCoursesWithoutSession($dates['startDate'], $dates['endDate']);
 createCourseSessions($courses, $administratorId, $dates['startDate'], $dates['endDate']);
 
 // Creates course sessions for the following month
-if (date("Y-m-d") >= date("Y-m-".OFFSET)) {
-    $dates = getMonthFirstAndLastDates(date("Y-m-d", api_strtotime(date("Y-m-01")." + 1 month")));
+$offsetDay = intval(substr($dates['endDate'], 8, 2)) - OFFSET;
+if (date("Y-m-d") >= date(substr($dates['endDate'], 0, 8) . $offsetDay)) {
+    $dates = getQuarterFirstAndLastDates(date("Y-m-d", api_strtotime(date("Y-m-01")." + 3 month")));
     // Get courses that don't have any session the next month
     $courses = CourseManager::getCoursesWithoutSession($dates['startDate'], $dates['endDate']);
     createCourseSessions($courses, $administratorId, $dates['startDate'], $dates['endDate']);

+ 27 - 0
main/css/base.css

@@ -5903,3 +5903,30 @@ ul.holder li.bit-box{
     height: auto !important;
     max-width: 100%;
 }
+
+/***    Draggable answer    ***/
+.question_options ul.exercise-draggable-answer{
+    float: left;
+    margin-bottom: 20px;
+    min-height: 2em;
+    min-width: 100%;
+}
+ul.exercise-draggable-answer li {
+    cursor: move;
+    float: left;
+    margin: 0 20px 20px 0;
+    padding: 5px;
+    text-align: center;
+    width: 200px;
+}
+
+.question_options .droppable {
+    float: left;
+    margin: 0 20px 20px 0;
+    padding: 20px;
+    text-align: center;
+    width: 240px
+}
+.question_options .droppable .gallery .exercise-draggable-answer-option {
+    margin-bottom: 15px;
+}

+ 224 - 0
main/exercice/Draggable.php

@@ -0,0 +1,224 @@
+<?php
+
+/* For licensing terms, see /license.txt */
+
+/**
+ * Draggable
+ *
+ * @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
+ */
+class Draggable extends Question
+{
+
+    static $typePicture = 'matching.png';
+    static $explanationLangVar = 'Draggable';
+
+    /**
+     * Class constructor
+     */
+    public function __construct()
+    {
+        parent::__construct();
+
+        $this->type = DRAGGABLE;
+        $this->isContent = $this->getIsContent();
+    }
+
+    /**
+     * Function which redefines Question::createAnswersForm
+     * @param FormValidator $form
+     */
+    public function createAnswersForm($form)
+    {
+        $defaults = array();
+        $nb_matches = $nb_options = 2;
+        $matches = array();
+
+        $answer = null;
+
+        if ($form->isSubmitted()) {
+            $nb_matches = $form->getSubmitValue('nb_matches');
+            $nb_options = $form->getSubmitValue('nb_options');
+
+            if (isset($_POST['lessMatches'])) {
+                $nb_matches--;
+            }
+
+            if (isset($_POST['moreMatches'])) {
+                $nb_matches++;
+            }
+
+            if (isset($_POST['lessOptions'])) {
+                $nb_options--;
+            }
+
+            if (isset($_POST['moreOptions'])) {
+                $nb_options++;
+            }
+        } else if (!empty($this->id)) {
+            $answer = new Answer($this->id);
+            $answer->read();
+
+            if (count($answer->nbrAnswers) > 0) {
+                $nb_matches = $nb_options = 0;
+                
+                for ($i = 1; $i <= $answer->nbrAnswers; $i++) {
+                    if ($answer->isCorrect($i)) {
+                        $nb_matches++;
+                        $defaults['answer[' . $nb_matches . ']'] = $answer->selectAnswer($i);
+                        $defaults['weighting[' . $nb_matches . ']'] = float_format($answer->selectWeighting($i), 1);
+                        $defaults['matches[' . $nb_matches . ']'] = $answer->correct[$i];
+                    } else {
+                        $nb_options++;
+                        $defaults['option[' . $nb_options . ']'] = $answer->selectAnswer($i);
+                    }
+                }
+            }
+        } else {
+            $defaults['answer[1]'] = get_lang('DefaultMakeCorrespond1');
+            $defaults['answer[2]'] = get_lang('DefaultMakeCorrespond2');
+            $defaults['matches[2]'] = '2';
+            $defaults['option[1]'] = get_lang('DefaultMatchingOptA');
+            $defaults['option[2]'] = get_lang('DefaultMatchingOptB');
+        }
+
+        for ($i = 1; $i <= $nb_matches; ++$i) {
+            $matches[$i] = $i;
+        }
+
+        $form->addElement('hidden', 'nb_matches', $nb_matches);
+        $form->addElement('hidden', 'nb_options', $nb_options);
+
+        // DISPLAY MATCHES
+        $html = '<table class="table table-striped table-hover">
+            <thead>
+                <tr>
+                    <th width="85%">' . get_lang('Answer') . '</th>
+                    <th width="15%">' . get_lang('MatchesTo') . '</th>
+                    <th width="10">' . get_lang('Weighting') . '</th>
+                </tr>
+            </thead>
+            <tbody>';
+
+        $form->addHeader(get_lang('MakeCorrespond'));
+        $form->addHtml($html);
+
+        if ($nb_matches < 1) {
+            $nb_matches = 1;
+            Display::display_normal_message(get_lang('YouHaveToCreateAtLeastOneAnswer'));
+        }
+
+        for ($i = 1; $i <= $nb_matches; ++$i) {
+            $renderer = &$form->defaultRenderer();
+
+            $renderer->setElementTemplate(
+                '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error -->{element}</td>',
+                "answer[$i]"
+            );
+
+            $renderer->setElementTemplate(
+                '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error -->{element}</td>',
+                "matches[$i]"
+            );
+
+            $renderer->setElementTemplate(
+                '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error -->{element}</td>',
+                "weighting[$i]"
+            );
+
+            $form->addHtml('<tr>');
+            $form->addText("answer[$i]", null);
+            $form->addSelect("matches[$i]", null, $matches);
+            $form->addText("weighting[$i]", null, true, ['value' => 10]);
+            $form->addHtml('</tr>');
+        }
+
+        $form->addHtml('</tbody></table>');
+        $group = array();
+
+        $renderer->setElementTemplate(
+            '<div class="form-group"><div class="col-sm-offset-2">{element}',
+            'lessMatches'
+        );
+        $renderer->setElementTemplate('{element}</div></div>', 'moreMatches');
+
+        global $text;
+
+        $group = [
+            $form->addButtonDelete(get_lang('DelElem'), 'lessMatches', true),
+            $form->addButtonCreate(get_lang('AddElem'), 'moreMatches', true),
+            $form->addButtonSave($text, 'submitQuestion', true)
+        ];
+
+        $form->addGroup($group);
+
+        if (!empty($this->id)) {
+            $form->setDefaults($defaults);
+        } else {
+            if ($this->isContent == 1) {
+                $form->setDefaults($defaults);
+            }
+        }
+
+        $form->setConstants(
+            [
+                'nb_matches' => $nb_matches,
+                'nb_options' => $nb_options
+            ]
+        );
+    }
+
+    /**
+     * Abstract function which creates the form to create / edit the answers of the question
+     * @param FormValidator $form
+     */
+    public function processAnswersCreation($form)
+    {
+        $nb_matches = $form->getSubmitValue('nb_matches');
+        $this->weighting = 0;
+        $position = 0;
+
+        $objAnswer = new Answer($this->id);
+
+        // Insert the options
+        for ($i = 1; $i <= $nb_matches; ++$i) {
+            $position++;
+
+            $objAnswer->createAnswer($position, 0, '', 0, $position);
+        }
+
+        // Insert the answers
+        for ($i = 1; $i <= $nb_matches; ++$i) {
+            $position++;
+
+            $answer = $form->getSubmitValue('answer[' . $i . ']');
+            $matches = $form->getSubmitValue('matches[' . $i . ']');
+            $weighting = $form->getSubmitValue('weighting[' . $i . ']');
+            $this->weighting += $weighting;
+            $objAnswer->createAnswer($answer, $matches, '', $weighting, $position);
+        }
+
+        $objAnswer->save();
+        $this->save();
+    }
+
+    /**
+     * Shows question title an description
+     * @param string $feedback_type
+     * @param int $counter
+     * @param float $score
+     * @return string
+     */
+    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>
+                <th>' . get_lang('ElementList') . '</th>
+                <th>' . get_lang('Status') . '</th>
+            </tr>';
+
+        return $header;
+    }
+
+}

+ 61 - 20
main/exercice/exercise.class.php

@@ -1890,7 +1890,7 @@ class Exercise
                     }
 					$class .= ' question-validate-btn'; // used to select it with jquery
                     $all_button = '&nbsp;<a href="javascript://" class="'.$class.'" onclick="validate_all(); ">'.$all_label.'</a>';
-                    $all_button .= '&nbsp;<span id="save_all_reponse"></span>';
+                    $all_button .= '&nbsp;' . Display::span(null, ['id' => 'save_all_reponse']);
                     $html .= $all_button;
                 }
             }
@@ -2772,6 +2772,8 @@ class Exercise
                         }
                     }
                     break;
+                case DRAGGABLE:
+                    //no break
                 case MATCHING:
                     if ($from_database) {
                         $sql = 'SELECT id, answer, id_auto
@@ -2824,20 +2826,38 @@ class Exercise
                                 if ($s_user_answer == $i_answer_correct_answer) {
                                     $questionScore += $i_answerWeighting;
                                     $totalScore += $i_answerWeighting;
-                                    if (isset($real_list[$i_answer_id])) {
-                                        $user_answer = '<span>'.$real_list[$i_answer_id].'</span>';
+                                    if ($answerType == DRAGGABLE) {
+                                        $user_answer = Display::label(get_lang('Correct'), 'success');
+                                    } else {
+                                        if (isset($real_list[$i_answer_id])) {
+                                            $user_answer = Display::span($real_list[$i_answer_id]);
+                                        }
                                     }
                                 } else {
-                                    $user_answer = '<span style="color: #FF0000; text-decoration: line-through;">'.$real_list[$s_user_answer].'</span>';
+                                    if ($answerType == DRAGGABLE) {
+                                        $user_answer = Display::label(get_lang('Incorrect'), 'danger');
+                                    } else {
+                                        $user_answer = Display::span(
+                                            $real_list[$s_user_answer],
+                                            ['style' => 'color: #FF0000; text-decoration: line-through;']
+                                        );
+                                    }
                                 }
+                            } elseif ($answerType == DRAGGABLE) {
+                                $user_answer = Display::label(get_lang('Incorrect'), 'danger');
                             }
 
                             if ($show_result) {
                                 echo '<tr>';
-                                echo '<td>'.$s_answer_label.'</td>';
-                                echo '<td>'.$user_answer;
-                                if (isset($real_list[$i_answer_correct_answer])) {
-                                    echo ' <b><span style="color: #008000;">'.$real_list[$i_answer_correct_answer].'</span></b> ';
+                                echo '<td>' . $s_answer_label . '</td>';
+                                echo '<td>' . $user_answer;
+                                if ($answerType == MATCHING) {
+                                    if (isset($real_list[$i_answer_correct_answer])) {
+                                        echo Display::span(
+                                            $real_list[$i_answer_correct_answer],
+                                            ['style' => 'color: #008000; font-weight: bold;']
+                                        );
+                                    }
                                 }
                                 echo '</td>';
                                 echo '</tr>';
@@ -2850,10 +2870,13 @@ class Exercise
                                 $questionScore += $answerWeighting;
                                 $totalScore += $answerWeighting;
 
-                                $user_answer = '<span>'.$answerMatching[$choice[$answerAutoId]].'</span>';
+                                $user_answer = Display::span($answerMatching[$choice[$answerAutoId]]);
                             } else {
                                 if (isset($answerMatching[$choice[$answerAutoId]])) {
-                                    $user_answer = '<span style="color: #FF0000; text-decoration: line-through;">'.$answerMatching[$choice[$answerAutoId]].'</span>';
+                                    $user_answer = Display::span(
+                                        $answerMatching[$choice[$answerAutoId]],
+                                        ['style' => 'color: #FF0000; text-decoration: line-through;']
+                                    );
                                 }
                             }
                             $matching[$answerAutoId] = $choice[$answerAutoId];
@@ -2951,7 +2974,13 @@ class Exercise
                 if ($from == 'exercise_result') {
                     if ($debug) error_log('Showing questions $from '.$from);
                     //display answers (if not matching type, or if the answer is correct)
-                    if ($answerType != MATCHING || $answerCorrect) {
+                    if (
+                        !in_array(
+                            $answerType,
+                            [MATCHING, DRAGGABLE]
+                        ) ||
+                        $answerCorrect
+                    ) {
                         if (
                             in_array(
                                 $answerType,
@@ -3218,10 +3247,15 @@ class Exercise
                             }
                         } elseif($answerType == MATCHING) {
                             echo '<tr>';
-                            echo '<td>' . $answerMatching[$answerId] . '</td>';
-                            echo '<td>' . $user_answer . ' / <b>'
-                                . '<span style="color: #008000;">' . $answerMatching[$answerCorrect] . '</span>'
-                                . '</b></td>';
+                            echo Display::tag('td', $answerMatching[$answerId]);
+                            echo Display::tag(
+                                'td',
+                                "$user_answer / " . Display::tag(
+                                    'strong',
+                                    $answerMatching[$answerCorrect],
+                                    ['style' => 'color: #008000; font-weight: bold;']
+                                )
+                            );
                             echo '</tr>';
                         }
                     }
@@ -3535,12 +3569,19 @@ class Exercise
                                 $answerComment
                             );
                             break;
+                        case DRAGGABLE:
+                            //no break
                         case MATCHING:
                             echo '<tr>';
-                            echo '<td>' . $answerMatching[$answerId] . '</td>';
-                            echo '<td>' . $user_answer . ' / <b>'
-                                . '<span style="color: #008000;">' . $answerMatching[$answerCorrect] . '</span></b>'
-                                . '</td>';
+                            echo Display::tag('td', $answerMatching[$answerId]);
+                            echo Display::tag(
+                                'td',
+                                "$user_answer / " . Display::tag(
+                                    'strong',
+                                    $answerMatching[$answerCorrect],
+                                    ['style' => 'color: #008000; font-weight: bold;']
+                                )
+                            );
                             echo '</tr>';
 
                             break;
@@ -3842,7 +3883,7 @@ class Exercise
                 } else {
                     Event::saveQuestionAttempt($questionScore, 0, $quesId, $exeId, 0, $this->id);
                 }
-            } elseif ($answerType == MATCHING) {
+            } elseif (in_array($answerType, [MATCHING, DRAGGABLE])) {
                 if (isset($matching)) {
                     foreach ($matching as $j => $val) {
                         Event::saveQuestionAttempt($questionScore, $val, $quesId, $exeId, $j, $this->id);

+ 1 - 1
main/exercice/exercise_show.php

@@ -343,7 +343,7 @@ foreach ($questionList as $questionId) {
 		$question_result = $objExercise->manage_answer($id, $questionId, $choice,'exercise_show', array(), false, true, $show_results, $objExercise->selectPropagateNeg());
 		$questionScore   = $question_result['score'];
 		$totalScore     += $question_result['score'];
-	} elseif ($answerType == MATCHING) {
+        } elseif (in_array($answerType, [MATCHING, DRAGGABLE])) {
         $question_result = $objExercise->manage_answer($id, $questionId, $choice,'exercise_show', array(), false, true, $show_results, $objExercise->selectPropagateNeg());
         $questionScore   = $question_result['score'];
         $totalScore     += $question_result['score'];

+ 2 - 0
main/exercice/exercise_submit.php

@@ -57,6 +57,8 @@ $htmlHeadXtra[] = api_get_js('epiclock/javascript/jquery.dateformat.min.js');
 $htmlHeadXtra[] = api_get_js('epiclock/javascript/jquery.epiclock.min.js');
 $htmlHeadXtra[] = api_get_js('epiclock/renderers/minute/epiclock.minute.js');
 
+$htmlHeadXtra[] = (new Template())->fetch('default/exercise/submit.js.tpl');
+
 // General parameters passed via POST/GET
 
 $learnpath_id = isset($_REQUEST['learnpath_id']) ? intval($_REQUEST['learnpath_id']) : 0;

+ 2 - 1
main/exercice/question.class.php

@@ -49,7 +49,8 @@ abstract class Question
         ),
         GLOBAL_MULTIPLE_ANSWER => array('global_multiple_answer.class.php' , 'GlobalMultipleAnswer'),
         CALCULATED_ANSWER => array('calculated_answer.class.php' , 'CalculatedAnswer'),
-        UNIQUE_ANSWER_IMAGE => ['UniqueAnswerImage.php', 'UniqueAnswerImage']
+        UNIQUE_ANSWER_IMAGE => ['UniqueAnswerImage.php', 'UniqueAnswerImage'],
+        DRAGGABLE => ['Draggable.php', 'Draggable']
         //MEDIA_QUESTION => array('media_question.class.php' , 'MediaQuestion')
     );
 

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

@@ -475,6 +475,7 @@ define('GLOBAL_MULTIPLE_ANSWER', 14);
 define('MEDIA_QUESTION', 15);
 define('CALCULATED_ANSWER', 16);
 define('UNIQUE_ANSWER_IMAGE', 17);
+define('DRAGGABLE', 18);
 
 //Some alias used in the QTI exports
 define('MCUA', 1);

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

@@ -3122,7 +3122,7 @@ class CourseManager
                         relation_type=" . COURSE_RELATION_TYPE_RRHH . " AND
                         access_url_id = " . api_get_current_access_url_id() . "";
         } else {
-            $sql = "SELECT course_code FROM $tbl_course_rel_user
+            $sql = "SELECT c_id FROM $tbl_course_rel_user
                     WHERE user_id = $hr_manager_id AND relation_type=" . COURSE_RELATION_TYPE_RRHH . " ";
         }
         $result = Database::query($sql);

+ 438 - 434
main/inc/lib/course_description.lib.php

@@ -14,55 +14,55 @@
  */
 class CourseDescription
 {
-	private $id;
+    private $id;
     private $course_id;
-	private $title;
-	private $content;
+    private $title;
+    private $content;
     private $session_id;
     private $description_type;
     private $progress;
 
-   	/**
-	 * Constructor
-	 */
-	public function __construct()
+    /**
+     * Constructor
+     */
+    public function __construct()
     {
 
     }
 
-	/**
-	 * Returns an array of objects of type CourseDescription corresponding to
+    /**
+     * Returns an array of objects of type CourseDescription corresponding to
      * a specific course, without session ids (session id = 0)
-	 *
-	 * @param int Course id
-	 * @return array Array of CourseDescriptions
-	 */
-	public static function get_descriptions($course_id)
+     *
+     * @param int Course id
+     * @return array Array of CourseDescriptions
+     */
+    public static function get_descriptions($course_id)
     {
-		// Get course code
-		$course_info = api_get_course_info_by_id($course_id);
+        // Get course code
+        $course_info = api_get_course_info_by_id($course_id);
         if (!empty($course_info)) {
             $course_id = $course_info['real_id'];
         } else {
             return array();
         }
-		$t_course_desc = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
-		$sql = "SELECT * FROM $t_course_desc
-		        WHERE c_id = $course_id AND session_id = '0'";
-		$sql_result = Database::query($sql);
-		$results = array();
-		while($row = Database::fetch_array($sql_result)) {
-			$desc_tmp = new CourseDescription();
-			$desc_tmp->set_id($row['id']);
-			$desc_tmp->set_title($row['title']);
-			$desc_tmp->set_content($row['content']);
-			$desc_tmp->set_session_id($row['session_id']);
-			$desc_tmp->set_description_type($row['description_type']);
-			$desc_tmp->set_progress($row['progress']);
-			$results[] = $desc_tmp;
-		}
-		return $results;
-	}
+        $t_course_desc = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+        $sql = "SELECT * FROM $t_course_desc
+              WHERE c_id = $course_id AND session_id = '0'";
+        $sql_result = Database::query($sql);
+        $results = array();
+        while ($row = Database::fetch_array($sql_result)) {
+            $desc_tmp = new CourseDescription();
+            $desc_tmp->set_id($row['id']);
+            $desc_tmp->set_title($row['title']);
+            $desc_tmp->set_content($row['content']);
+            $desc_tmp->set_session_id($row['session_id']);
+            $desc_tmp->set_description_type($row['description_type']);
+            $desc_tmp->set_progress($row['progress']);
+            $results[] = $desc_tmp;
+        }
+        return $results;
+    }
 
 
     /**
@@ -70,87 +70,90 @@ class CourseDescription
      * first you must set session_id property with the object CourseDescription
      * @return array
      */
-	public function get_description_data()
+    public function get_description_data()
     {
-		$tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
-		$condition_session = api_get_session_condition($this->session_id, true, true);
+        $tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+        $condition_session = api_get_session_condition($this->session_id, true, true);
         $course_id = api_get_course_int_id();
-		$sql = "SELECT * FROM $tbl_course_description
+        $sql = "SELECT * FROM $tbl_course_description
 		        WHERE c_id = $course_id $condition_session
 		        ORDER BY id ";
-		$rs = Database::query($sql);
-		$data = array();
-		while ($description = Database::fetch_array($rs)) {
-			$data['descriptions'][$description['id']] = Security::remove_XSS($description, STUDENT);
-			//reload titles to ensure we have the last version (after edition)
-			//$data['default_description_titles'][$description['id']] = Security::remove_XSS($description['title'], STUDENT);
-		}
-		return $data;
-	}
-
-	/**
+        $rs = Database::query($sql);
+        $data = array();
+        while ($description = Database::fetch_array($rs)) {
+            $data['descriptions'][$description['id']] = Security::remove_XSS($description, STUDENT);
+            //reload titles to ensure we have the last version (after edition)
+            //$data['default_description_titles'][$description['id']] = Security::remove_XSS($description['title'], STUDENT);
+        }
+        return $data;
+    }
+
+    /**
      * Get all data of course description by session id,
      * first you must set session_id property with the object CourseDescription
      * @deprecated
      * @return array
      */
-	public function get_description_history($description_type)
+    public function get_description_history($description_type)
     {
-		$tbl_stats_item_property = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY);
-		$tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
+        $tbl_stats_item_property = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY);
+        $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
 
-		$description_id = $this->get_id_by_description_type($description_type);
-		$item_property_id = api_get_item_property_id($course_id, TOOL_COURSE_DESCRIPTION, $description_id);
+        $description_id = $this->get_id_by_description_type($description_type);
+        $item_property_id = api_get_item_property_id(api_get_course_id(), TOOL_COURSE_DESCRIPTION, $description_id);
 
-		$course_id = api_get_course_int_id();
+        $course_id = api_get_course_int_id();
 
-		$sql = "SELECT tip.id, tip.course_id, tip.item_property_id, tip.title, tip.content, tip.progress, tip.lastedit_date, tip.session_id
+        $sql = "SELECT tip.id, tip.course_id, tip.item_property_id, tip.title, tip.content, tip.progress, tip.lastedit_date, tip.session_id
 				FROM $tbl_stats_item_property tip INNER JOIN $tbl_item_property ip
-				ON ip.tool = '".TOOL_COURSE_DESCRIPTION."' AND ip.id = tip.item_property_id
-				WHERE ip.c_id = $course_id AND tip.course_id = '$course_id' AND tip.session_id = '".intval($this->session_id)."'
+				ON ip.tool = '" . TOOL_COURSE_DESCRIPTION . "' AND ip.id = tip.item_property_id
+				WHERE ip.c_id = $course_id AND tip.course_id = '$course_id' AND tip.session_id = '" . intval($this->session_id) . "'
 				ORDER BY tip.lastedit_date DESC";
 
-		$rs = Database::query($sql);
-		$data = array();
-		while ($description = Database::fetch_array($rs)) {
-			$data['descriptions'][] = $description;
-		}
-		return $data;
-	}
+        $rs = Database::query($sql);
+        $data = array();
+        while ($description = Database::fetch_array($rs)) {
+            $data['descriptions'][] = $description;
+        }
+        return $data;
+    }
 
-	/**
+    /**
      * Get all data by description and session id,
      * first you must set session_id property with the object CourseDescription
-     * @param 	int		description type
+     * @param    int        description type
      * @param   string  course code (optional)
-     * @param	int		session id (optional)
+     * @param    int        session id (optional)
      * @return array
      */
-	public function get_data_by_description_type($description_type, $course_code = '', $session_id = null)
-    {
-		$tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
-		$course_id = api_get_course_int_id();
-
-		if (!isset($session_id)) {
-			$session_id = $this->session_id;
-		}
-		$condition_session = api_get_session_condition($session_id);
-		if (!empty($course_code)) {
-			$course_info = api_get_course_info($course_code);
+    public function get_data_by_description_type(
+        $description_type,
+        $course_code = '',
+        $session_id = null
+    ) {
+        $tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+        $course_id = api_get_course_int_id();
+
+        if (!isset($session_id)) {
+            $session_id = $this->session_id;
+        }
+        $condition_session = api_get_session_condition($session_id);
+        if (!empty($course_code)) {
+            $course_info = api_get_course_info($course_code);
             $course_id = $course_info['real_id'];
-		}
+        }
         $description_type = intval($description_type);
-		$sql = "SELECT * FROM $tbl_course_description
+        $sql = "SELECT * FROM $tbl_course_description
 		        WHERE c_id = $course_id AND description_type='$description_type' $condition_session ";
-		$rs = Database::query($sql);
-		$data = array();
-		if ($description = Database::fetch_array($rs)) {
-			$data['description_title']	 = $description['title'];
-			$data['description_content'] = $description['content'];
-			$data['progress'] 			 = $description['progress'];
-		}
-		return $data;
-	}
+        $rs = Database::query($sql);
+        $data = array();
+        if ($description = Database::fetch_array($rs)) {
+            $data['description_title'] = $description['title'];
+            $data['description_content'] = $description['content'];
+            $data['progress'] = $description['progress'];
+        }
+        return $data;
+    }
 
     /**
      * @param int $id
@@ -160,149 +163,147 @@ class CourseDescription
      */
     public function get_data_by_id($id, $course_code = '', $session_id = null)
     {
-		$tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
-		$course_id = api_get_course_int_id();
-
-		if (!isset($session_id)) {
-			$session_id = $this->session_id;
-		}
-		$condition_session = api_get_session_condition($session_id);
-		if (!empty($course_code)) {
-			$course_info = api_get_course_info($course_code);
+        $tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+        $course_id = api_get_course_int_id();
+
+        if (!isset($session_id)) {
+            $session_id = $this->session_id;
+        }
+        $condition_session = api_get_session_condition($session_id);
+        if (!empty($course_code)) {
+            $course_info = api_get_course_info($course_code);
             $course_id = $course_info['real_id'];
-		}
+        }
         $id = intval($id);
-		$sql = "SELECT * FROM $tbl_course_description
+        $sql = "SELECT * FROM $tbl_course_description
 		        WHERE c_id = $course_id AND id='$id' $condition_session ";
-		$rs = Database::query($sql);
-		$data = array();
-		if ($description = Database::fetch_array($rs)) {
-            $data['description_type']	 = $description['description_type'];
-			$data['description_title']	 = $description['title'];
-			$data['description_content'] = $description['content'];
-			$data['progress'] 			 = $description['progress'];
-		}
+        $rs = Database::query($sql);
+        $data = array();
+        if ($description = Database::fetch_array($rs)) {
+            $data['description_type'] = $description['description_type'];
+            $data['description_title'] = $description['title'];
+            $data['description_content'] = $description['content'];
+            $data['progress'] = $description['progress'];
+        }
 
-		return $data;
-	}
+        return $data;
+    }
 
 
-	/**
+    /**
      * Get maximum description type by session id,
      * first you must set session_id properties with the object CourseDescription
      * @return  int  maximum description time adding one
      */
-	public function get_max_description_type()
+    public function get_max_description_type()
     {
-		$tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+        $tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
         $course_id = api_get_course_int_id();
 
-		$sql = "SELECT MAX(description_type) as MAX FROM $tbl_course_description
-		        WHERE c_id = $course_id AND session_id='".$this->session_id."'";
-		$rs  = Database::query($sql);
-		$max = Database::fetch_array($rs);
-		$description_type = $max['MAX']+1;
-		if ($description_type < ADD_BLOCK) {
-			$description_type = ADD_BLOCK;
-		}
-		return $description_type;
-	}
-
-	/**
+        $sql = "SELECT MAX(description_type) as MAX FROM $tbl_course_description
+		        WHERE c_id = $course_id AND session_id='" . $this->session_id . "'";
+        $rs = Database::query($sql);
+        $max = Database::fetch_array($rs);
+        $description_type = $max['MAX'] + 1;
+        if ($description_type < ADD_BLOCK) {
+            $description_type = ADD_BLOCK;
+        }
+        return $description_type;
+    }
+
+    /**
      * Insert a description to the course_description table,
      * first you must set description_type, title, content, progress and
      * session_id properties with the object CourseDescription
      * @return  int  affected rows
      */
-	public function insert()
+    public function insert()
     {
         if (empty($this->course_id)) {
-		    $course_id = api_get_course_int_id();
+            $course_id = api_get_course_int_id();
         } else {
             $course_id = $this->course_id;
         }
-		$tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
-		$sql = "INSERT IGNORE INTO $tbl_course_description SET
+        $tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+        $sql = "INSERT IGNORE INTO $tbl_course_description SET
 				c_id 				=  $course_id,
-				description_type	= '".intval($this->description_type)."',
-				title 				= '".Database::escape_string($this->title)."',
-				content 			= '".Database::escape_string($this->content)."',
-				progress 			= '".intval($this->progress)."',
-				session_id = '".intval($this->session_id)."' ";
-		$result = Database::query($sql);
-		$last_id = Database::insert_id();
-		$affected_rows = Database::affected_rows($result);
-		if ($last_id > 0) {
+				description_type	= '" . intval($this->description_type) . "',
+				title 				= '" . Database::escape_string($this->title) . "',
+				content 			= '" . Database::escape_string($this->content) . "',
+				progress 			= '" . intval($this->progress) . "',
+				session_id = '" . intval($this->session_id) . "' ";
+        $result = Database::query($sql);
+        $last_id = Database::insert_id();
+        $affected_rows = Database::affected_rows($result);
+        if ($last_id > 0) {
             $sql = "UPDATE $tbl_course_description SET id = iid WHERE iid = $last_id";
             Database::query($sql);
 
-			//insert into item_property
-			api_item_property_update(
-				api_get_course_info(),
-				TOOL_COURSE_DESCRIPTION,
-				$last_id,
-				'CourseDescriptionAdded',
-				api_get_user_id()
-			);
-		}
+            //insert into item_property
+            api_item_property_update(
+                api_get_course_info(),
+                TOOL_COURSE_DESCRIPTION,
+                $last_id,
+                'CourseDescriptionAdded',
+                api_get_user_id()
+            );
+        }
 
-		return $affected_rows;
-	}
+        return $affected_rows;
+    }
 
-	/**
+    /**
      * Insert a row like history inside track_e_item_property table
      * first you must set description_type, title, content, progress and
      * session_id properties with the object CourseDescription
-     * @param 	int 	description type
-     * @return  int		affected rows
+     * @param    int    description type
+     * @return  int        affected rows
      */
-	public function insert_stats($description_type)
+    public function insert_stats($description_type)
     {
-		$tbl_stats_item_property = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY);
-		$description_id = $this->get_id_by_description_type($description_type);
-		$course_id = api_get_real_course_id();
-		$course_code = api_get_course_id();
-		$item_property_id = api_get_item_property_id($course_code, TOOL_COURSE_DESCRIPTION, $description_id);
-		$sql = "INSERT IGNORE INTO $tbl_stats_item_property SET
-				c_id				= ".api_get_course_int_id().",
+        $tbl_stats_item_property = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY);
+        $description_id = $this->get_id_by_description_type($description_type);
+        $course_id = api_get_real_course_id();
+        $course_code = api_get_course_id();
+        $item_property_id = api_get_item_property_id($course_code,
+            TOOL_COURSE_DESCRIPTION, $description_id);
+        $sql = "INSERT IGNORE INTO $tbl_stats_item_property SET
+				c_id				= " . api_get_course_int_id() . ",
 				course_id 			= '$course_id',
 			 	item_property_id 	= '$item_property_id',
-			 	title 				= '".Database::escape_string($this->title)."',
-			 	content 			= '".Database::escape_string($this->content)."',
-			 	progress 			= '".intval($this->progress)."',
-			 	lastedit_date 		= '".date('Y-m-d H:i:s')."',
-			 	lastedit_user_id 	= '".api_get_user_id()."',
-			 	session_id			= '".intval($this->session_id)."'";
-		$result = Database::query($sql);
-		$affected_rows = Database::affected_rows($result);
-
-        $sql = "UPDATE $tbl_course_description SET id = iid WHERE iid = $last_id";
-        Database::query($sql);
-
-		return $affected_rows;
-	}
-
-	/**
+			 	title 				= '" . Database::escape_string($this->title) . "',
+			 	content 			= '" . Database::escape_string($this->content) . "',
+			 	progress 			= '" . intval($this->progress) . "',
+			 	lastedit_date 		= '" . date('Y-m-d H:i:s') . "',
+			 	lastedit_user_id 	= '" . api_get_user_id() . "',
+			 	session_id			= '" . intval($this->session_id) . "'";
+        $result = Database::query($sql);
+        $affected_rows = Database::affected_rows($result);
+
+        return $affected_rows;
+    }
+
+    /**
      * Update a description, first you must set description_type, title, content, progress
      * and session_id properties with the object CourseDescription
-     * @return int	affected rows
+     * @return int    affected rows
      */
-	public function update()
+    public function update()
     {
-		$tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
-		$sql = "UPDATE $tbl_course_description SET
-                    title       = '".Database::escape_string($this->title)."',
-                    content     = '".Database::escape_string($this->content)."',
-                    progress    = '".$this->progress."'
+        $tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+        $sql = "UPDATE $tbl_course_description SET
+                    title       = '" . Database::escape_string($this->title) . "',
+                    content     = '" . Database::escape_string($this->content) . "',
+                    progress    = '" . $this->progress . "'
                 WHERE
-                    id = '".intval($this->id)."' AND
-                    session_id = '".$this->session_id."' AND
-                    c_id = ".api_get_course_int_id();
-		$result = Database::query($sql);
-		$affected_rows = Database::affected_rows($result);
-
-		if ($this->id > 0) {
-			//insert into item_property
+                    id = '" . intval($this->id) . "' AND
+                    session_id = '" . $this->session_id . "' AND
+                    c_id = " . api_get_course_int_id();
+        $result = Database::query($sql);
+        $affected_rows = Database::affected_rows($result);
+
+        if ($this->id > 0) {
+            //insert into item_property
             api_item_property_update(
                 api_get_course_info(),
                 TOOL_COURSE_DESCRIPTION,
@@ -310,28 +311,28 @@ class CourseDescription
                 'CourseDescriptionUpdated',
                 api_get_user_id()
             );
-		}
-		return $affected_rows;
-	}
+        }
+        return $affected_rows;
+    }
 
-	/**
+    /**
      * Delete a description, first you must set description_type and session_id
      * properties with the object CourseDescription
-     * @return int	affected rows
+     * @return int    affected rows
      */
-	public function delete()
+    public function delete()
     {
-		$tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
-		$course_id = api_get_course_int_id();
-		$sql = "DELETE FROM $tbl_course_description
+        $tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+        $course_id = api_get_course_int_id();
+        $sql = "DELETE FROM $tbl_course_description
 			 	WHERE
 			 	    c_id = $course_id AND
-			 	    id = '".intval($this->id)."' AND
-			 	    session_id = '".intval($this->session_id)."'";
-		$result = Database::query($sql);
-		$affected_rows = Database::affected_rows($result);
-		if ($this->id > 0) {
-			//insert into item_property
+			 	    id = '" . intval($this->id) . "' AND
+			 	    session_id = '" . intval($this->session_id) . "'";
+        $result = Database::query($sql);
+        $affected_rows = Database::affected_rows($result);
+        if ($this->id > 0) {
+            //insert into item_property
             api_item_property_update(
                 api_get_course_info(),
                 TOOL_COURSE_DESCRIPTION,
@@ -339,164 +340,167 @@ class CourseDescription
                 'CourseDescriptionDeleted',
                 api_get_user_id()
             );
-		}
+        }
 
-		return $affected_rows;
-	}
+        return $affected_rows;
+    }
 
-	/**
-	 * Get description id by description type
-	 * @param int description type
-	 * @return int description id
-	 */
-	public function get_id_by_description_type($description_type)
+    /**
+     * Get description id by description type
+     * @param int description type
+     * @return int description id
+     */
+    public function get_id_by_description_type($description_type)
     {
-		$tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+        $tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
         $course_id = api_get_course_int_id();
 
-		$sql = "SELECT id FROM $tbl_course_description
-		        WHERE c_id = $course_id AND description_type = '".intval($description_type)."'";
-		$rs  = Database::query($sql);
-		$row = Database::fetch_array($rs);
-		$description_id = $row['id'];
-		return $description_id;
-	}
-
-	/**
-	 * get thematic progress in porcent for a course,
-	 * first you must set session_id property with the object CourseDescription
-	 * @param bool		true for showing a icon about the progress, false otherwise (optional)
-	 * @param int		Description type (optional)
-	 * @return string   img html
-	 */
-	 public function get_progress_porcent($with_icon = false, $description_type = THEMATIC_ADVANCE)
-     {
-	 	$tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
-	 	$session_id = intval($session_id);
+        $sql = "SELECT id FROM $tbl_course_description
+		        WHERE c_id = $course_id AND description_type = '" . intval($description_type) . "'";
+        $rs = Database::query($sql);
+        $row = Database::fetch_array($rs);
+        $description_id = $row['id'];
+        return $description_id;
+    }
+
+    /**
+     * get thematic progress in porcent for a course,
+     * first you must set session_id property with the object CourseDescription
+     * @param bool        true for showing a icon about the progress, false otherwise (optional)
+     * @param int        Description type (optional)
+     * @return string   img html
+     */
+    public function get_progress_porcent(
+        $with_icon = false,
+        $description_type = THEMATIC_ADVANCE
+    ) {
+        $tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+        $session_id = api_get_session_id();
         $course_id = api_get_course_int_id();
 
-		$sql = "SELECT progress FROM $tbl_course_description
+        $sql = "SELECT progress FROM $tbl_course_description
 		        WHERE
 		            c_id = $course_id AND
-		            description_type = '".intval($description_type)."' AND
-		            session_id = '".intval($this->session_id)."' ";
-		$rs  = Database::query($sql);
-		$progress = '';
-		$img = '';
-		$title = '0%';
-		$image = 'level_0.png';
-		if (Database::num_rows($rs) > 0) {
-			$row = Database::fetch_array($rs);
-			$progress = $row['progress'].'%';
-			$image = 'level_'.$row['progress'].'.png';
-		}
-		if ($with_icon) {
-			$img = Display::return_icon($image,get_lang('ThematicAdvance'),array('style'=>'vertical-align:middle'));
-		}
-		$progress = $img.$progress;
-		return $progress;
-	 }
-
-	/**
-	 * Get description titles by default
-	 * @return array
-	 */
-	public function get_default_description_title()
+		            description_type = '" . intval($description_type) . "' AND
+		            session_id = '" . intval($this->session_id) . "' ";
+        $rs = Database::query($sql);
+        $progress = '';
+        $img = '';
+        $title = '0%';
+        $image = 'level_0.png';
+        if (Database::num_rows($rs) > 0) {
+            $row = Database::fetch_array($rs);
+            $progress = $row['progress'] . '%';
+            $image = 'level_' . $row['progress'] . '.png';
+        }
+        if ($with_icon) {
+            $img = Display::return_icon($image, get_lang('ThematicAdvance'),
+                array('style' => 'vertical-align:middle'));
+        }
+        $progress = $img . $progress;
+        return $progress;
+    }
+
+    /**
+     * Get description titles by default
+     * @return array
+     */
+    public function get_default_description_title()
     {
-		$default_description_titles = array();
-		$default_description_titles[1]= get_lang('GeneralDescription');
-		$default_description_titles[2]= get_lang('Objectives');
-		$default_description_titles[3]= get_lang('Topics');
-		$default_description_titles[4]= get_lang('Methodology');
-		$default_description_titles[5]= get_lang('CourseMaterial');
-		$default_description_titles[6]= get_lang('HumanAndTechnicalResources');
-		$default_description_titles[7]= get_lang('Assessment');
-
-		$default_description_titles[8]= get_lang('Other');
-		return $default_description_titles;
-	}
-
-	/**
-	 * Get description titles editable by default
-	 * @return array
-	 */
-	public function get_default_description_title_editable()
+        $default_description_titles = array();
+        $default_description_titles[1] = get_lang('GeneralDescription');
+        $default_description_titles[2] = get_lang('Objectives');
+        $default_description_titles[3] = get_lang('Topics');
+        $default_description_titles[4] = get_lang('Methodology');
+        $default_description_titles[5] = get_lang('CourseMaterial');
+        $default_description_titles[6] = get_lang('HumanAndTechnicalResources');
+        $default_description_titles[7] = get_lang('Assessment');
+
+        $default_description_titles[8] = get_lang('Other');
+        return $default_description_titles;
+    }
+
+    /**
+     * Get description titles editable by default
+     * @return array
+     */
+    public function get_default_description_title_editable()
     {
-		$default_description_title_editable = array();
-		$default_description_title_editable[1] = true;
-		$default_description_title_editable[2] = true;
-		$default_description_title_editable[3] = true;
-		$default_description_title_editable[4] = true;
-		$default_description_title_editable[5] = true;
-		$default_description_title_editable[6] = true;
-		$default_description_title_editable[7] = true;
-		//$default_description_title_editable[8] = true;
-		return $default_description_title_editable;
-	}
-
-	/**
-	 * Get description icons by default
-	 * @return array
-	 */
-	public function get_default_description_icon()
+        $default_description_title_editable = array();
+        $default_description_title_editable[1] = true;
+        $default_description_title_editable[2] = true;
+        $default_description_title_editable[3] = true;
+        $default_description_title_editable[4] = true;
+        $default_description_title_editable[5] = true;
+        $default_description_title_editable[6] = true;
+        $default_description_title_editable[7] = true;
+        //$default_description_title_editable[8] = true;
+        return $default_description_title_editable;
+    }
+
+    /**
+     * Get description icons by default
+     * @return array
+     */
+    public function get_default_description_icon()
     {
-		$default_description_icon = array();
-		$default_description_icon[1]= 'info.png';
-		$default_description_icon[2]= 'objective.png';
-		$default_description_icon[3]= 'topics.png';
-		$default_description_icon[4]= 'strategy.png';
-		$default_description_icon[5]= 'laptop.png';
-		$default_description_icon[6]= 'teacher.png';
-		$default_description_icon[7]= 'assessment.png';
-		//$default_description_icon[8]= 'porcent.png';
-		$default_description_icon[8]= 'wizard.png';
-		return $default_description_icon;
-	}
-
-	/**
-	 * Get questions by default for help
-	 * @return array
-	 */
-	public function get_default_question()
+        $default_description_icon = array();
+        $default_description_icon[1] = 'info.png';
+        $default_description_icon[2] = 'objective.png';
+        $default_description_icon[3] = 'topics.png';
+        $default_description_icon[4] = 'strategy.png';
+        $default_description_icon[5] = 'laptop.png';
+        $default_description_icon[6] = 'teacher.png';
+        $default_description_icon[7] = 'assessment.png';
+        //$default_description_icon[8]= 'porcent.png';
+        $default_description_icon[8] = 'wizard.png';
+        return $default_description_icon;
+    }
+
+    /**
+     * Get questions by default for help
+     * @return array
+     */
+    public function get_default_question()
     {
-		$question = array();
-		$question[1]= get_lang('GeneralDescriptionQuestions');
-		$question[2]= get_lang('ObjectivesQuestions');
-		$question[3]= get_lang('TopicsQuestions');
-		$question[4]= get_lang('MethodologyQuestions');
-		$question[5]= get_lang('CourseMaterialQuestions');
-		$question[6]= get_lang('HumanAndTechnicalResourcesQuestions');
-		$question[7]= get_lang('AssessmentQuestions');
-		//$question[8]= get_lang('ThematicAdvanceQuestions');
-		return $question;
-	}
-
-	/**
-	 * Get informations by default for help
-	 * @return array
-	 */
-	public function get_default_information()
+        $question = array();
+        $question[1] = get_lang('GeneralDescriptionQuestions');
+        $question[2] = get_lang('ObjectivesQuestions');
+        $question[3] = get_lang('TopicsQuestions');
+        $question[4] = get_lang('MethodologyQuestions');
+        $question[5] = get_lang('CourseMaterialQuestions');
+        $question[6] = get_lang('HumanAndTechnicalResourcesQuestions');
+        $question[7] = get_lang('AssessmentQuestions');
+        //$question[8]= get_lang('ThematicAdvanceQuestions');
+        return $question;
+    }
+
+    /**
+     * Get informations by default for help
+     * @return array
+     */
+    public function get_default_information()
     {
-		$information = array();
-		$information[1]= get_lang('GeneralDescriptionInformation');
-		$information[2]= get_lang('ObjectivesInformation');
-		$information[3]= get_lang('TopicsInformation');
-		$information[4]= get_lang('MethodologyInformation');
-		$information[5]= get_lang('CourseMaterialInformation');
-		$information[6]= get_lang('HumanAndTechnicalResourcesInformation');
-		$information[7]= get_lang('AssessmentInformation');
-		//$information[8]= get_lang('ThematicAdvanceInformation');
-		return $information;
-	}
-
-	/**
-	 * Set description id
-	 * @return void
-	 */
-	public function set_id($id)
+        $information = array();
+        $information[1] = get_lang('GeneralDescriptionInformation');
+        $information[2] = get_lang('ObjectivesInformation');
+        $information[3] = get_lang('TopicsInformation');
+        $information[4] = get_lang('MethodologyInformation');
+        $information[5] = get_lang('CourseMaterialInformation');
+        $information[6] = get_lang('HumanAndTechnicalResourcesInformation');
+        $information[7] = get_lang('AssessmentInformation');
+        //$information[8]= get_lang('ThematicAdvanceInformation');
+        return $information;
+    }
+
+    /**
+     * Set description id
+     * @return void
+     */
+    public function set_id($id)
     {
-		$this->id = $id;
-	}
+        $this->id = $id;
+    }
 
     /**
      * Set description's course id
@@ -508,102 +512,102 @@ class CourseDescription
         $this->course_id = intval($id);
     }
 
-   	/**
-	 * Set description title
-	 * @return void
-	 */
-	public function set_title($title)
+    /**
+     * Set description title
+     * @return void
+     */
+    public function set_title($title)
     {
-		$this->title = $title;
-	}
+        $this->title = $title;
+    }
 
     /**
-	 * Set description content
-	 * @return void
-	 */
-	public function set_content($content)
+     * Set description content
+     * @return void
+     */
+    public function set_content($content)
     {
-		$this->content = $content;
-	}
-
-	/**
-	 * Set description session id
-	 * @return void
-	 */
-	public function set_session_id($session_id)
+        $this->content = $content;
+    }
+
+    /**
+     * Set description session id
+     * @return void
+     */
+    public function set_session_id($session_id)
     {
-		$this->session_id = $session_id;
-	}
-
-   	/**
-	 * Set description type
-	 * @return void
-	 */
-	public function set_description_type($description_type)
+        $this->session_id = $session_id;
+    }
+
+    /**
+     * Set description type
+     * @return void
+     */
+    public function set_description_type($description_type)
     {
-		$this->description_type = $description_type;
-	}
-
-	/**
-	 * Set progress of a description
-	 * @return void
-	 */
-	public function set_progress($progress)
+        $this->description_type = $description_type;
+    }
+
+    /**
+     * Set progress of a description
+     * @return void
+     */
+    public function set_progress($progress)
     {
-		$this->progress = $progress;
-	}
-
-	/**
-	 * get description id
-	 * @return int
-	 */
-	public function get_id()
+        $this->progress = $progress;
+    }
+
+    /**
+     * get description id
+     * @return int
+     */
+    public function get_id()
     {
-		return $this->id;
-	}
-
-   	/**
-	 * get description title
-	 * @return string
-	 */
-	public function get_title()
+        return $this->id;
+    }
+
+    /**
+     * get description title
+     * @return string
+     */
+    public function get_title()
     {
-		return $this->title;
-	}
-
-   	/**
-	 * get description content
-	 * @return string
-	 */
-	public function get_content()
+        return $this->title;
+    }
+
+    /**
+     * get description content
+     * @return string
+     */
+    public function get_content()
     {
-		return $this->content;
-	}
-
-	/**
-	 * get session id
-	 * @return int
-	 */
-	public function get_session_id()
+        return $this->content;
+    }
+
+    /**
+     * get session id
+     * @return int
+     */
+    public function get_session_id()
     {
-		return $this->session_id;
-	}
-
-   	/**
-	 * get description type
-	 * @return int
-	 */
-	public function get_description_type()
+        return $this->session_id;
+    }
+
+    /**
+     * get description type
+     * @return int
+     */
+    public function get_description_type()
     {
-		return $this->description_type;
-	}
-
-	/**
-	 * get progress of a description
-	 * @return int
-	 */
-	public function get_progress()
+        return $this->description_type;
+    }
+
+    /**
+     * get progress of a description
+     * @return int
+     */
+    public function get_progress()
     {
-		return $this->progress;
-	}
+        return $this->progress;
+    }
 }

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

@@ -1754,8 +1754,8 @@ class Display
         if (!empty($list)) {
             $html = '<dl class="dl-horizontal">';
             foreach ($list as $item) {
-                $html .= '<dt>'.$item['title'].'</dd>';
-                $html .= '<dd>'.$item['content'].'</dt>';
+                $html .= '<dt>' . $item['title'] . '</dt>';
+                $html .= '<dd>' . $item['content'] . '</dd>';
             }
             $html .= '</dl>';
         }

+ 127 - 2
main/inc/lib/exercise.lib.php

@@ -104,8 +104,14 @@ class ExerciseLib
             // on the right side are called answers
             $num_suggestions = 0;
 
-            if ($answerType == MATCHING) {
-                $s .= '<table class="table table-hover table-striped">';
+            if (in_array($answerType, [MATCHING, DRAGGABLE])) {
+                if ($answerType == DRAGGABLE) {
+                    $s .= '<div class="ui-widget ui-helper-clearfix">
+                        <div class="clearfix">
+                        <ul class="exercise-draggable-answer ui-helper-reset ui-helper-clearfix">';
+                } else {
+                    $s .= '<table class="table table-hover table-striped">';
+                }
                 // Iterate through answers
                 $x = 1;
                 //mark letters for each answer
@@ -908,6 +914,94 @@ class ExerciseLib
                         }  // end if()
                         $matching_correct_answer++;
                     }
+                } elseif ($answerType == DRAGGABLE) {
+                    if ($answerCorrect != 0) {
+                        $parsed_answer = $answer;
+                        $windowId = $questionId . '_' . $lines_count;
+
+                        $s .= '<li class="thumbnail" id="' . $windowId . '">';
+                        $s .= Display::div(
+                            $parsed_answer,
+                            [
+                                'id' => "window_$windowId",
+                                'class' => "window{$questionId}_question_draggable exercise-draggable-answer-option"
+                            ]
+                        );
+                        $selectedValue = 0;
+                        $draggableSelectOptions = [];
+
+                        foreach ($select_items as $key => $val) {
+                            if ($debug_mark_answer) {
+                                if ($val['id'] == $answerCorrect) {
+                                    $selectedValue = $val['id'];
+                                }
+                            }
+
+                            if (
+                                isset($user_choice[$matching_correct_answer]) &&
+                                $val['id'] == $user_choice[$matching_correct_answer]['answer']
+                            ) {
+                                $selectedValue = $val['id'];
+                            }
+
+                            $draggableSelectOptions[$val['id']] = $val['letter'];
+                        }
+
+                        $s .= Display::select(
+                            "choice[$questionId][$numAnswer]",
+                            $draggableSelectOptions,
+                            $selectedValue,
+                            [
+                                'id' => "window_{$windowId}_select",
+                                'class' => 'select_option',
+                                'style' => 'display: none;'
+                            ],
+                            false
+                        );
+
+                        if (!empty($answerCorrect) && !empty($selectedValue)) {
+                            $s .= <<<JAVASCRIPT
+                                <script>
+                                    $(function() {
+                                        DraggableAnswer.deleteItem(
+                                            $('#{$questionId}_{$selectedValue}'),
+                                            $('#drop_$windowId')
+                                        );
+                                    });
+                                </script>
+JAVASCRIPT;
+                        }
+
+                        if (isset($select_items[$lines_count])) {
+                            $s .= Display::div(
+                                Display::tag(
+                                    'b',
+                                    $select_items[$lines_count]['letter']
+                                ) . $select_items[$lines_count]['answer'],
+                                [
+                                    'id' => "window_{$windowId}_answer",
+                                    'style' => 'display: none;'
+                                ]
+                            );
+                        } else {
+                            $s .= '&nbsp;';
+                        }
+
+                        $lines_count++;
+
+                        if (($lines_count - 1) == $num_suggestions) {
+                            while (isset($select_items[$lines_count])) {
+                                $s .= Display::tag('b', $select_items[$lines_count]['letter']);
+                                $s .= $select_items[$lines_count]['answer'];
+
+                                $lines_count++;
+                            }
+                        }
+
+                        $matching_correct_answer++;
+
+                        $s .= '</li>';
+                    }
                 }
             }    // end for()
 
@@ -921,6 +1015,37 @@ class ExerciseLib
                 }
             }
 
+            if ($answerType == DRAGGABLE) {
+                $s .= "</ul></div>";
+
+                $counterAnswer = 1;
+                
+                $s .= '<div class="col-xs-12"><div class="row">';
+
+                for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
+                    $answerCorrect = $objAnswerTmp->isCorrect($answerId);
+                    $windowId = $questionId . '_' . $counterAnswer;
+
+                    if ($answerCorrect) {
+                        $s .= Display::div(
+                            $counterAnswer,
+                            [
+                                'id' => "drop_$windowId",
+                                'class' => 'droppable col-sm-4 well'
+                            ]
+                        );
+
+                        $counterAnswer++;
+                    }
+                }
+
+                $s .= '</div></div>';
+            }
+
+            if ($answerType == MATCHING) {
+                $s .= '</div>';
+            }
+
             $s .= '</div>';
 
             // destruction of the Answer object

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

@@ -4489,9 +4489,9 @@ class UserManager
                 $sql = "INSERT IGNORE INTO $userRelUserTable(user_id, friend_user_id, relation_type) "
                     . "VALUES ($subscribedUserId, $userId, $relationType)";
 
-                Database::query($sql);
+                $result = Database::query($sql);
 
-                $affectedRows = Database::affected_rows();
+                $affectedRows = Database::affected_rows($result);
             }
         }
 

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

@@ -7231,4 +7231,6 @@ $RequestSubmitted = "Your request has been submitted.";
 $RequestFailed = "We are not able to fulfill your request at this time. Please contact your administrator.";
 $InternalLogin = "Internal login";
 $AlreadyLoggedIn = "You are already logged in";
+$Draggable = "Draggable";
+$Incorrect = "Incorrect";
 ?>

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

@@ -7235,4 +7235,6 @@ $SaveBadge = "Guardar insignia";
 $BadgeMeasuresXPixelsInPNG = "Medidas de la insignia 200x200 píxeles en formato PNG";
 $SetTutor = "Hacer tutor";
 $UniqueAnswerImage = "Respuesta de imagen única";
+$Draggable = "Arrastrable";
+$Incorrect = "Incorrecto";
 ?>

+ 95 - 0
main/template/default/exercise/submit.js.tpl

@@ -0,0 +1,95 @@
+<script>
+    var DraggableAnswer = {
+        gallery: null,
+        trash: null,
+        deleteItem: function (item, insertHere) {
+            if (insertHere.find(".exercise-draggable-answer-option").length > 0) {
+                return false;
+            }
+
+            item.fadeOut(function () {
+                var $list = $('<div class="gallery ui-helper-reset"/>').appendTo(insertHere);
+
+                item.find('a.btn').remove();
+
+                var droppedId = item.attr('id'),
+                    dropedOnId = insertHere.attr('id'),
+                    originSelectId = 'window_' + droppedId + '_select',
+                    value = dropedOnId.split('_')[2];
+
+                $('#' + originSelectId + ' option')
+                    .filter(function (index) {
+                        return index === parseInt(value);
+                    })
+                    .attr("selected", true);
+
+                var recycleButton = $('<a>')
+                        .attr('href', '#')
+                        .addClass('btn btn-default btn-xs')
+                        .append(
+                            "{{ "Undo" | get_lang }} ",
+                            $('<i>').addClass('fa fa-undo')
+                        )
+                        .on('click', function (e) {
+                            e.preventDefault();
+
+                            var liParent = $(this).parent();
+
+                            DraggableAnswer.recycleItem(liParent);
+                        });
+
+                item.append(recycleButton).appendTo($list).fadeIn();
+            });
+        },
+        recycleItem: function (item) {
+            item.fadeOut(function () {
+                item
+                    .find('a.btn')
+                    .remove()
+                    .end()
+                    .find("img")
+                    .end()
+                    .appendTo(DraggableAnswer.gallery)
+                    .fadeIn();
+            });
+
+            var droppedId = item.attr('id'),
+                originSelectId = 'window_' + droppedId + '_select';
+
+            $('#' + originSelectId + ' option:first').attr('selected', 'selected');
+        },
+        init: function (gallery, trash) {
+            this.gallery = gallery;
+            this.trash = trash;
+
+            $("li", DraggableAnswer.gallery).draggable({
+                cancel: "a.ui-icon",
+                revert: "invalid",
+                containment: "document",
+                helper: "clone",
+                cursor: "move"
+            });
+
+            DraggableAnswer.trash.droppable({
+                accept: ".exercise-draggable-answer > li",
+                hoverClass: "ui-state-active",
+                drop: function (e, ui) {
+                    DraggableAnswer.deleteItem(ui.draggable, $(this));
+                }
+            });
+
+            DraggableAnswer.gallery.droppable({
+                drop: function (e, ui) {
+                    DraggableAnswer.recycleItem(ui.draggable, $(this));
+                }
+            });
+        }
+    };
+
+    $(document).on('ready', function () {
+        DraggableAnswer.init(
+            $(".exercise-draggable-answer"),
+            $(".droppable")
+        );
+    });
+</script>