Julio Montoya 6 years ago
parent
commit
a059620d08

BIN
app/Resources/public/css/themes/chamilo/images/letterhead.png


+ 0 - 1
main/course_description/index.php

@@ -101,7 +101,6 @@ switch ($action) {
         $descriptionController->destroy($id);
         break;
     case 'listing':
-        //no break
     default:
         $descriptionController->listing();
 }

+ 13 - 13
main/exercise/ReadingComprehension.php

@@ -53,7 +53,7 @@ class ReadingComprehension extends UniqueAnswer
 
     /**
      * Indicates how show the question list.
-     * 1 = all in one page; 2 = one per page (default)
+     * 1 = all in one page; 2 = one per page (default).
      *
      * @var int
      */
@@ -197,6 +197,18 @@ class ReadingComprehension extends UniqueAnswer
         return $select_level;
     }
 
+    /**
+     * @param int $type
+     *
+     * @return ReadingComprehension
+     */
+    public function setExerciseType($type)
+    {
+        $this->exerciseType = (int) $type;
+
+        return $this;
+    }
+
     /**
      * @param $wordsCount
      * @param $turns
@@ -216,16 +228,4 @@ class ReadingComprehension extends UniqueAnswer
         $view->assign('exercise_type', $this->exerciseType);
         $view->display($template);
     }
-
-    /**
-     * @param int $type
-     *
-     * @return ReadingComprehension
-     */
-    public function setExerciseType($type)
-    {
-        $this->exerciseType = (int) $type;
-
-        return $this;
-    }
 }

+ 10 - 0
main/exercise/admin.php

@@ -366,6 +366,16 @@ if ($inATest) {
     if ($objExercise->added_in_lp()) {
         echo Display::return_message(get_lang('AddedToLPCannotBeAccessed'), 'warning');
     }
+
+    if ($editQuestion && $objQuestion->existsInAnotherExercises()) {
+        echo Display::return_message(
+            Display::returnFontAwesomeIcon('exclamation-triangle"')
+                .get_lang('ThisQuestionExistsInAnotherExercisesWarning'),
+            'warning',
+            false
+        );
+    }
+
     echo '<div class="alert alert-info">';
     echo sprintf(
         get_lang('XQuestionsWithTotalScoreY'),

+ 22 - 0
main/exercise/question.class.php

@@ -2324,6 +2324,28 @@ abstract class Question
         return '<br />'.Display::return_message($this->feedback, 'normal', false);
     }
 
+    /**
+     * Check if this question exists in another exercise.
+     *
+     * @throws \Doctrine\ORM\Query\QueryException
+     *
+     * @return mixed
+     */
+    public function existsInAnotherExercises()
+    {
+        $em = Database::getManager();
+
+        $count = $em
+            ->createQuery('
+                SELECT COUNT(qq.iid) FROM ChamiloCourseBundle:CQuizRelQuestion qq
+                WHERE qq.questionId = :id
+            ')
+            ->setParameters(['id' => (int) $this->id])
+            ->getSingleScalarResult();
+
+        return $count > 1;
+    }
+
     /**
      * Resizes a picture || Warning!: can only be called after uploadPicture,
      * or if picture is already available in object.

+ 3 - 0
main/exercise/question_pool.php

@@ -125,6 +125,9 @@ if ($is_allowedToEdit) {
         // Adds the question ID represented by $recup into the list of questions for the current exercise
         $objExercise->addToList($recup);
         Session::write('objExercise', $objExercise);
+        Display::addFlash(
+            Display::return_message(get_lang('ItemAdded'), 'success')
+        );
     } elseif (isset($_POST['recup']) && is_array($_POST['recup']) && $fromExercise) {
         $list_recup = $_POST['recup'];
         foreach ($list_recup as $course_id => $question_data) {

+ 3 - 0
main/gradebook/gradebook_add_link.php

@@ -154,6 +154,7 @@ if (isset($typeSelected) && $typeSelected != '0') {
     }
 }
 
+<<<<<<< HEAD
 $action_details = '';
 $current_id = 0;
 if (isset($_GET['selectcat'])) {
@@ -172,6 +173,8 @@ $logInfo = [
 ];
 Event::registerLog($logInfo);
 
+=======
+>>>>>>> de59e9d72409ccb3dda93caa5fe09c8fba7dfe75
 $interbreadcrumb[] = [
     'url' => Category::getUrl().'selectcat='.$selectCat,
     'name' => get_lang('Gradebook'),

+ 0 - 1
main/gradebook/gradebook_edit_cat.php

@@ -85,7 +85,6 @@ if ($form->validate()) {
 }
 $selectcat = isset($_GET['selectcat']) ? (int) $_GET['selectcat'] : 0;
 
-
 $action_details = '';
 $current_id = 0;
 if (isset($_GET['editcat'])) {

+ 2 - 1
main/inc/lib/CoursesAndSessionsCatalog.class.php

@@ -469,8 +469,9 @@ class CoursesAndSessionsCatalog
      * @param string $date  (optional) The date of sessions
      * @param array  $limit
      *
-     * @return array The session list
      * @throws Exception
+     *
+     * @return array The session list
      */
     public static function browseSessions($date = null, $limit = [])
     {

+ 6 - 6
main/inc/lib/TicketManager.php

@@ -360,8 +360,8 @@ class TicketManager
             'sys_lastedit_datetime' => $now,
             'source' => $source,
             'assigned_last_user' => $assignedUserId,
-            'subject' => Database::escape_string($subject),
-            'message' => Database::escape_string($content),
+            'subject' => $subject,
+            'message' => $content,
         ];
 
         if (!empty($course_id)) {
@@ -653,14 +653,14 @@ class TicketManager
 
         $params = [
             'ticket_id' => $ticketId,
-            'subject' => Database::escape_string($subject),
-            'message' => Database::escape_string($content),
-            'ip_address' => Database::escape_string(api_get_real_ip()),
+            'subject' => $subject,
+            'message' => $content,
+            'ip_address' => api_get_real_ip(),
             'sys_insert_user_id' => $userId,
             'sys_insert_datetime' => $now,
             'sys_lastedit_user_id' => $userId,
             'sys_lastedit_datetime' => $now,
-            'status' => Database::escape_string($status),
+            'status' => $status,
         ];
         $messageId = Database::insert($table_support_messages, $params);
         if ($messageId) {

+ 8 - 4
main/inc/lib/api.lib.php

@@ -5086,7 +5086,7 @@ function api_max_sort_value($user_course_category, $user_id)
  *
  * @return string the formated time
  */
-function api_time_to_hms($seconds)
+function api_time_to_hms($seconds, $space = ':')
 {
     // $seconds = -1 means that we have wrong data in the db.
     if ($seconds == -1) {
@@ -5108,6 +5108,10 @@ function api_time_to_hms($seconds)
     // How many seconds
     $sec = floor($seconds - ($hours * 3600) - ($min * 60));
 
+    if ($hours < 10) {
+        $hours = "0$hours";
+    }
+
     if ($sec < 10) {
         $sec = "0$sec";
     }
@@ -5116,7 +5120,7 @@ function api_time_to_hms($seconds)
         $min = "0$min";
     }
 
-    return "$hours:$min:$sec";
+    return $hours.$space.$min.$space.$sec;
 }
 
 /* FILE SYSTEM RELATED FUNCTIONS */
@@ -6242,7 +6246,7 @@ function api_is_element_in_the_session($tool, $element_id, $session_id = null)
     }
     $course_id = api_get_course_int_id();
 
-    $sql = "SELECT session_id FROM $table_tool 
+    $sql = "SELECT session_id FROM $table_tool
             WHERE c_id = $course_id AND $key_field =  ".$element_id;
     $rs = Database::query($sql);
     if ($element_session_id = Database::result($rs, 0, 0)) {
@@ -8067,7 +8071,7 @@ function api_get_password_checker_js($usernameInputId, $passwordInputId)
     ];
 
     $js = api_get_asset('pwstrength-bootstrap/dist/pwstrength-bootstrap.min.js');
-    $js .= "<script>    
+    $js .= "<script>
     var errorMessages = {
         password_to_short : \"".get_lang('PasswordIsTooShort')."\",
         same_as_username : \"".get_lang('YourPasswordCannotBeTheSameAsYourUsername')."\"

+ 8 - 3
main/inc/lib/banner.lib.php

@@ -184,18 +184,23 @@ function getCustomTabs()
  *
  * @param string $theme The name of the theme folder from web/css/themes/
  *
+ * @param bool $responsive add class img-responsive
  * @return string HTML string with logo as an HTML element
  */
-function return_logo($theme = '')
+function return_logo($theme = '', $responsive = true)
 {
     $siteName = api_get_setting('siteName');
+    $class = 'img-responsive';
+    if(!$responsive){
+        $class='';
+    }
 
     return ChamiloApi::getPlatformLogo(
         $theme,
         [
             'title' => $siteName,
-            'class' => 'img-responsive',
-            'id' => 'header-logo',
+            'class' => $class,
+            'id' => 'header-logo'
         ]
     );
 }

+ 24 - 4
main/inc/lib/events.lib.php

@@ -1144,6 +1144,9 @@ class Event
      * @param int   $lp_id
      * @param array $course
      * @param int   $session_id
+     *
+     * @return bool
+     *
      */
     public static function delete_student_lp_events(
         $user_id,
@@ -1156,7 +1159,14 @@ class Event
         $lpInteraction = Database::get_course_table(TABLE_LP_IV_INTERACTION);
         $lpObjective = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
 
+        if (empty($course)) {
+            return false;
+        }
+
         $course_id = $course['real_id'];
+        $user_id = (int) $user_id;
+        $lp_id = (int) $lp_id;
+        $session_id = (int) $session_id;
 
         if (empty($course_id)) {
             $course_id = api_get_course_int_id();
@@ -1172,10 +1182,6 @@ class Event
             TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING
         );
 
-        $user_id = (int) $user_id;
-        $lp_id = (int) $lp_id;
-        $session_id = (int) $session_id;
-
         //Make sure we have the exact lp_view_id
         $sql = "SELECT id FROM $lp_view_table
                 WHERE
@@ -1202,6 +1208,18 @@ class Event
             Database::query($sql);
         }
 
+        if (api_get_configuration_value('lp_minimum_time')) {
+            $sql = "DELETE FROM track_e_access_complete
+                    WHERE 
+                        tool = 'learnpath' AND 
+                        c_id = $course_id AND 
+                        tool_id = $lp_id AND
+                        user_id = $user_id AND
+                        session_id = $session_id
+                    ";
+            Database::query($sql);
+        }
+
         $sql = "DELETE FROM $lp_view_table
                 WHERE
                     c_id = $course_id AND
@@ -1247,6 +1265,8 @@ class Event
             $course_id,
             $session_id
         );
+
+        return true;
     }
 
     /**

+ 25 - 12
main/inc/lib/pdf.lib.php

@@ -343,17 +343,17 @@ class PDF
     /**
      * Converts an html string to PDF.
      *
-     * @param string $document_html  valid html
-     * @param string $css            CSS content of a CSS file
-     * @param string $pdf_name       pdf name
-     * @param string $course_code    course code
+     * @param string $document_html valid html
+     * @param string $css CSS content of a CSS file
+     * @param string $pdf_name pdf name
+     * @param string $course_code course code
      *                               (if you are using html that are located in the document tool you must provide this)
-     * @param string $outputMode     the MPDF output mode can be:
-     * @param bool   $saveInFile
+     * @param string $outputMode the MPDF output mode can be:
+     * @param bool $saveInFile
      * @param string $fileToSave
-     * @param bool   $returnHtml
-     * @param bool   $addDefaultCss
-     * @param bool   $completeHeader
+     * @param bool $returnHtml
+     * @param bool $addDefaultCss
+     * @param bool $completeHeader
      *
      * 'I' (print on standard output),
      * 'D' (download file) (this is the default value),
@@ -361,6 +361,7 @@ class PDF
      * 'S' (return as a string)
      *
      * @return string Web path
+     * @throws MpdfException
      */
     public function content_to_pdf(
         $document_html,
@@ -461,7 +462,11 @@ class PDF
         }
 
         if (!empty($css)) {
-            $this->pdf->WriteHTML($css, 1);
+            try {
+                $this->pdf->WriteHTML($css, 1);
+            } catch (MpdfException $e) {
+                error_log($e);
+            }
         }
 
         if ($addDefaultCss) {
@@ -472,11 +477,19 @@ class PDF
             ];
             foreach ($basicStyles as $style) {
                 $cssContent = file_get_contents($style);
-                $this->pdf->WriteHTML($cssContent, 1);
+                try {
+                    $this->pdf->WriteHTML($cssContent, 1);
+                } catch (MpdfException $e) {
+                    error_log($e);
+                }
             }
         }
 
-        $this->pdf->WriteHTML($document_html);
+        try {
+            $this->pdf->WriteHTML($document_html);
+        } catch (MpdfException $e) {
+            error_log($e);
+        }
 
         if (empty($pdf_name)) {
             $output_file = 'pdf_'.date('Y-m-d-his').'.pdf';

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

@@ -716,7 +716,9 @@ class Template
 
         // Logo
         $logo = return_logo($this->theme);
+        $logoPdf = return_logo($this->theme,false);
         $this->assign('logo', $logo);
+        $this->assign('logo_pdf', $logoPdf);
         $this->assign('show_media_element', 1);
     }
 

+ 6 - 6
main/inc/lib/tracking.lib.php

@@ -6793,10 +6793,10 @@ class Tracking
     }
 
     /**
-     * @param int  $userId
-     * @param int  $courseId
-     * @param int  $sessionId
-     * @param int  $toolId
+     * @param int $userId
+     * @param int $courseId
+     * @param int $sessionId
+     * @param int $toolId
      *
      * @return array
      */
@@ -6841,7 +6841,7 @@ class Tracking
                     c_id = $courseId AND
                     login_as = 0
                 ORDER BY date_reg ASC
-                LIMIT 0,1";
+                LIMIT 1";
         $rs = Database::query($sql);
 
         $firstConnection = '';
@@ -6861,7 +6861,7 @@ class Tracking
                     c_id = $courseId AND
                     login_as = 0
                 ORDER BY date_reg DESC
-                LIMIT 0,1";
+                LIMIT 1";
         $rs = Database::query($sql);
         $lastConnection = '';
         if (Database::num_rows($rs) > 0) {

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

@@ -8323,4 +8323,12 @@ $NewExerciseAttemptDisabled = "The portal do not allowed to start new test for t
 $PersonalDataOfficer = "Personal data officer";
 $MaxNumberSubscribedStudentsReached = "The maximum number of student has already been reached, it is not possible to subscribe more student.";
 $UserXAddedToCourseX = "User %s has been registered to course %s";
+$LpMinTime = "Minimum time (minutes)";
+$TimeSpentInLp = "Time spent in the learning path";
+$IHaveFinishedTheLessonsNotifyTheTeacher = "I have finished the lessons, notify the teacher";
+$TheStudentXHasFinishedTheCourseYLearningPaths = "The student <b>%s</b> has finished the learning paths from the course <b>%s</b>";
+$ThisQuestionExistsInAnotherExercisesWarning = "This question is used in another exercises. If you continue its edition, the changes will affect all exercises that contain this question.";
+$TimeSpentTimeRequired = "Time spent / Time required";
+$ProgressSpentInLp = "Progress";
+$InSessionXYouHadTheFollowingResults = "In session %s, you had the following results";
 ?>

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

@@ -8255,4 +8255,6 @@ $QuestionForNextClass = "Questions pour la classe suivante";
 $NewExerciseAttemptDisabled = "Le portail ne permet pas le démarrage d'exercice pour le moment, revenez un peu plus tard pour essayer à nouveau";
 $PersonalDataOfficer = "Délégué à la protection des données personnelles";
 $MaxNumberSubscribedStudentsReached = "Le nombre maximum d'apprenants a déjà été atteint, il n'est pas possible d'en inscrire de nouveau";
+$UserXAddedToCourseX = "L'utilisateur %s a été inscrit au cours %s";
+$InSessionXYouHadTheFollowingResults = "Dans la formation %s, vous avez obtenu les résultats suivants:";
 ?>

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

@@ -8354,4 +8354,10 @@ $LpMinTimeDescription = "Tiempo mínimo (en minutos) que el estudiante debe perm
 $LpMinTimeWarning = "No ha alcanzado el tiempo mínimo en la lección.";
 $YouHaveSpentXTime = "Has invertido %s tiempo";
 $TimeSpentInLp = "Tiempo en la lección";
+$StudentXFinishedLp = "El estudiante %s ha finalizado sus lecciones.";
+$IHaveFinishedTheLessonsNotifyTheTeacher = "He finalizado las lecciones del curso. Notificar al profesor.";
+$TheStudentXHasFinishedTheCourseYLearningPaths = "El estudiante <strong>%s</strong> ha finalizado las lecciones del curso <strong>%s</strong>";
+$ThisQuestionExistsInAnotherExercisesWarning = "Esta pregunta se usa en otros ejercicios. Si continúa con su edición, los cambios afectarán a todos los ejercicios que contienen esta pregunta.";
+$TimeSpentTimeRequired = "Tiempo invertido / tiempo requerido";
+$ProgressSpentInLp = "Progreso contenidos";
 ?>

+ 9 - 0
main/lp/lp_ajax_save_item.php

@@ -491,6 +491,15 @@ function save_item(
         $return .= "update_stats();";
     }
 
+    $logInfo = [
+        'tool' => TOOL_LEARNPATH,
+        'tool_id' => $myLP->get_id(),
+        'tool_id_detail' => $myLP->get_current_item_id(),
+        'action' => 'save_item',
+        'info' => '',
+    ];
+    Event::registerLog($logInfo);
+
     // To be sure progress is updated.
     $myLP->save_last();
 

+ 13 - 22
main/lp/lp_list.php

@@ -171,7 +171,7 @@ if ($filteredCategoryId) {
 $test_mode = api_get_setting('server_type');
 $showBlockedPrerequisite = api_get_configuration_value('show_prerequisite_as_blocked');
 $allowLpChamiloExport = api_get_configuration_value('allow_lp_chamilo_export');
-
+$allowMinTime = api_get_configuration_value('lp_minimum_time');
 $user = api_get_user_entity($userId);
 $ending = true;
 
@@ -218,7 +218,7 @@ foreach ($categories as $item) {
 
     $listData = [];
     $lpTimeList = [];
-    if (api_get_configuration_value('lp_minimum_time')) {
+    if ($allowMinTime) {
         $lpTimeList = Tracking::getCalculateTime($userId, api_get_course_int_id(), api_get_session_id());
     }
 
@@ -404,7 +404,7 @@ foreach ($categories as $item) {
 
             $dsp_time = '';
             $linkMinTime = '';
-            if (api_get_configuration_value('lp_minimum_time')) {
+            if ($allowMinTime) {
                 // Time info
                 // Minimum time (in minutes) to pass the learning path
                 $accumulateWorkTime = learnpath::getAccumulateWorkTimePrerequisite($id, api_get_course_int_id());
@@ -412,29 +412,12 @@ foreach ($categories as $item) {
                     // TT --- Tiempo total del curso
                     $accumulateWorkTimeTotal = learnpath::getAccumulateWorkTimeTotal(api_get_course_int_id());
 
-                    // Spent time (in seconds) so far in the learning path
-                    /*$lpTime = Tracking::get_time_spent_in_lp(
-                        $userId,
-                        api_get_course_id(),
-                        [$id],
-                        api_get_session_id()
-                    );*/
-
                     $lpTime = isset($lpTimeList[TOOL_LEARNPATH][$id]) ? $lpTimeList[TOOL_LEARNPATH][$id] : 0;
 
                     // Connect with the plugin_licences_course_session table
                     // which indicates what percentage of the time applies
                     $perc = 100;
                     $tc = $accumulateWorkTimeTotal;
-                    /*if (!empty($current_session) && $current_session != 0) {
-                        $sql = "SELECT hours, perc FROM plugin_licences_course_session WHERE session_id = $current_session";
-                        $res = Database::query($sql);
-                        if (Database::num_rows($res) > 0) {
-                            $aux = Database::fetch_assoc($res);
-                            $perc = $aux['perc'];
-                            $tc = ($aux['hours'] * 60);
-                        }
-                    }*/
 
                     // Percentage of the learning paths
                     $pl = 0;
@@ -453,9 +436,16 @@ foreach ($categories as $item) {
                                 $accumulateWorkTime * 60
                             )
                         );
-                        $linkMinTime .= '<b>'.api_time_to_hms($lpTime).' / '.api_time_to_hms($accumulateWorkTime * 60).'</b>';
+                        $linkMinTime .= '&nbsp;<b>'.api_time_to_hms($lpTime).' / '.api_time_to_hms($accumulateWorkTime * 60).'</b>';
                     } else {
-                        $linkMinTime = sprintf(get_lang('YouHaveSpentXTime'), api_time_to_hms($lpTime));
+                        $linkMinTime = Display::return_icon(
+                            'check.png',
+                            get_lang('LpMinTimeWarning').' - '.api_time_to_hms($lpTime).' / '.api_time_to_hms(
+                                $accumulateWorkTime * 60
+                            )
+                        );
+                        $linkMinTime .= '&nbsp;<b>'.api_time_to_hms($lpTime).' / '.api_time_to_hms($accumulateWorkTime * 60).'</b>';
+                        //$linkMinTime = sprintf(get_lang('YouHaveSpentXTime'), api_time_to_hms($lpTime));
                     }
 
                     // Calculate the percentage exceeded of the time for the "exceeding the minimum time" bar
@@ -1023,6 +1013,7 @@ $template->assign('introduction', $introduction);
 $template->assign('data', $data);
 $template->assign('lp_is_shown', $lpIsShown);
 $template->assign('filtered_category', $filteredCategoryId);
+$template->assign('allow_min_time', $allowMinTime);
 $templateName = $template->get_template('learnpath/list.tpl');
 $content = $template->fetch($templateName);
 $template->assign('content', $content);

+ 2 - 16
main/lp/lp_view.php

@@ -567,16 +567,6 @@ if (api_get_configuration_value('lp_minimum_time')) {
     $perc = 100;
     // Time from the course
     $tc = $timeTotalCourse;
-    if (!empty($sessionId) && $sessionId != 0) {
-        /*$sql = "SELECT hours, perc FROM plugin_licences_course_session WHERE session_id = $sessionId";
-        $res = Database::query($sql);
-        if (Database::num_rows($res) > 0) {
-            $aux = Database::fetch_assoc($res);
-            $perc = $aux['perc'];
-            $tc = $aux['hours'] * 60;
-        }*/
-    }
-
     // Percentage of the learning paths
     $pl = 0;
     if (!empty($timeTotalCourse)) {
@@ -590,12 +580,6 @@ if (api_get_configuration_value('lp_minimum_time')) {
         $lpMinTime = '('.$time_min.' min)';
     }
 
-    /*$lpTime = Tracking::get_time_spent_in_lp(
-        $user_id,
-        $course_code,
-        [$_SESSION['oLP']->lp_id],
-        $sessionId
-    );*/
     $lpTimeList = Tracking::getCalculateTime($user_id, api_get_course_int_id(), api_get_session_id());
     $lpTime = isset($lpTimeList[TOOL_LEARNPATH][$lp_id]) ? (int) $lpTimeList[TOOL_LEARNPATH][$lp_id] : 0;
 
@@ -614,6 +598,8 @@ if (api_get_configuration_value('lp_minimum_time')) {
     $template->assign('hour', $hour);
     $template->assign('minute', date('i', $lpTime));
     $template->assign('second', date('s', $lpTime));
+
+    $template->assign('hour_min', api_time_to_hms($timeLp*60, '</div><div class="divider">:</div><div>'));
 }
 
 $template->assign('lp_accumulate_work_time', $lpMinTime);

+ 226 - 0
main/mySpace/myStudents.php

@@ -212,6 +212,221 @@ $user_id = isset($_GET['user_id']) && !empty($_GET['user_id']) ? (int) $_GET['us
 $action = isset($_GET['action']) ? $_GET['action'] : '';
 
 switch ($action) {
+    case 'export_to_pdf':
+        $sessionToExport = $sId = isset($_GET['session_to_export']) ? (int) $_GET['session_to_export'] : 0;
+        $sessionInfo = api_get_session_info($sessionToExport);
+        if (empty($sessionInfo)) {
+            api_not_allowed(true);
+        }
+        $courses = Tracking::get_courses_list_from_session($sessionToExport);
+        $timeSpent = 0;
+        $numberVisits = 0;
+        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
+        $progress = 0;
+        foreach ($courses as $course) {
+            $courseId = $course['c_id'];
+            $timeSpent += Tracking::get_time_spent_on_the_course($student_id, $courseId, $sessionToExport);
+
+            $sql = 'SELECT DISTINCT count(course_access_id) as count
+                    FROM '.$table.'
+                    WHERE
+                        user_id = '.$student_id.' AND
+                        c_id = '.$courseId.' AND
+                        session_id = '.$sessionToExport.'
+                    ORDER BY login_course_date ASC';
+            $result = Database::query($sql);
+            $row = Database::fetch_array($result);
+            $numberVisits += $row['count'];
+            $progress += Tracking::get_avg_student_progress($student_id, $course['code'], [], $sessionToExport);
+        }
+
+        $average = round($progress / count($courses), 1);
+        $average = empty($average) ? '0%' : $average.'%';
+
+        $attendance = new Attendance();
+
+        $table = new HTML_Table(['class' => 'data_table']);
+        $column = 0;
+        $row = 0;
+        $headers = [
+            get_lang('TimeSpent'),
+            get_lang('NumberOfVisits'),
+            get_lang('Progress'),
+        ];
+
+        foreach ($headers as $header) {
+            $table->setHeaderContents($row, $column, $header);
+            $column++;
+        }
+        $table->setCellContents(1, 0, api_time_to_hms($timeSpent));
+        $table->setCellContents(1, 1, $numberVisits);
+        $table->setCellContents(1, 2, $average);
+
+        $courseTable = '';
+
+
+        if (!empty($courses)) {
+            $courseTable .= '<table class="data_table">';
+            $courseTable .= '<thead>';
+            $courseTable .= '<tr>
+                    <th>'.get_lang('Course').'</th>
+                    <th>'.get_lang('Time').'</th>
+                    <th>'.get_lang('Progress').'</th>
+                    <th>'.get_lang('Score').'</th>
+                </tr>';
+            $courseTable .= '</thead>';
+            $courseTable .= '<tbody>';
+
+            $totalCourseTime = 0;
+            $totalAttendance = [0, 0];
+            $totalScore = 0;
+            $totalProgress = 0;
+            $gradeBookTotal = [0, 0];
+            $totalEvaluations = '0/0 (0%)';
+            $totalCourses = count($courses);
+            $scoreDisplay = ScoreDisplay::instance();
+
+            foreach ($courses as $course) {
+                $courseId = $course['c_id'];
+                $courseInfoItem = api_get_course_info_by_id($courseId);
+                $courseId = $courseInfoItem['real_id'];
+                $courseCodeItem = $courseInfoItem['code'];
+
+                $isSubscribed = CourseManager::is_user_subscribed_in_course(
+                    $student_id,
+                    $courseCodeItem,
+                    true,
+                    $sId
+                );
+
+                if ($isSubscribed) {
+                    $timeInSeconds = Tracking::get_time_spent_on_the_course(
+                        $user_info['user_id'],
+                        $courseId,
+                        $sessionToExport
+                    );
+                    $totalCourseTime += $timeInSeconds;
+                    $time_spent_on_course = api_time_to_hms($timeInSeconds);
+
+                    $progress = Tracking::get_avg_student_progress(
+                        $user_info['user_id'],
+                        $courseCodeItem,
+                        [],
+                        $sId
+                    );
+
+                    $totalProgress += $progress;
+
+                    $score = Tracking::get_avg_student_score(
+                        $user_info['user_id'],
+                        $courseCodeItem,
+                        [],
+                        $sId
+                    );
+
+                    if (is_numeric($score)) {
+                        $totalScore += $score;
+                    }
+
+                    $progress = empty($progress) ? '0%' : $progress.'%';
+                    $score = empty($score) ? '0%' : $score.'%';
+
+                    $courseTable .= '<tr>
+                        <td ><a href="'.$courseInfoItem['course_public_url'].'?id_session='.$sId.'">'.
+                            $courseInfoItem['title'].'</a></td>
+                        <td >'.$time_spent_on_course.'</td>
+                        <td >'.$progress.'</td>
+                        <td >'.$score.'</td>';
+                    $courseTable .= '</tr>';
+                }
+            }
+
+            $totalAttendanceFormatted = $scoreDisplay->display_score($totalAttendance);
+            $totalScoreFormatted = $scoreDisplay->display_score([$totalScore / $totalCourses, 100], SCORE_AVERAGE);
+            $totalProgressFormatted = $scoreDisplay->display_score(
+                [$totalProgress / $totalCourses, 100],
+                SCORE_AVERAGE
+            );
+            $totalEvaluations = $scoreDisplay->display_score($gradeBookTotal);
+            $totalTimeFormatted = api_time_to_hms($totalCourseTime);
+            $courseTable .= '
+            <tr>
+                <th>'.get_lang('Total').'</th>
+                <th>'.$totalTimeFormatted.'</th>
+                <th>'.$totalProgressFormatted.'</th>
+                <th>'.$totalScoreFormatted.'</th>
+            </tr>';
+            $courseTable .= '</tbody></table>';
+        }
+
+        $studentInfo = api_get_user_info($student_id);
+
+        $tpl = new Template('', false, false, false, true, false, false);
+        $tpl->assign('title', $sessionInfo['name']);
+        $tpl->assign('student', $studentInfo['complete_name']);
+        $tpl->assign('table_progress', $table->toHtml());
+        $tpl->assign('subtitle', sprintf(
+            get_lang('InSessionXYouHadTheFollowingResults'),
+            $sessionInfo['name']
+        ));
+        $tpl->assign('table_course', $courseTable);
+        $template = $tpl->fetch($tpl->get_template('my_space/pdf_export_student.tpl'));
+
+        $content = ''.$template;
+
+
+        $params = [
+            'pdf_title' => get_lang('Resume'),
+            //'course_code' => api_get_course_id(),
+            'session_info' => $sessionInfo,
+            'course_info' => '',
+            'pdf_date' => '',
+            'student_info' => $studentInfo,
+            'show_grade_generated_date' => true,
+            'show_real_course_teachers' => false,
+            'show_teacher_as_myself' => false,
+            'orientation' => 'P',
+        ];
+
+        $pdf = new PDF('A4', $params['orientation'], $params);
+        try {
+            $theme = $tpl->theme;
+            $themeName = empty($theme) ? api_get_visual_theme() : $theme;
+            $themeDir = \Template::getThemeDir($theme);
+            $customLetterhead = $themeDir."images/letterhead.png";
+            $urlPathLetterhead = api_get_path(SYS_CSS_PATH)."$customLetterhead";
+
+            $urlWebLetterhead = '#FFFFFF';
+            $fullPage = false;
+            if (file_exists($urlPathLetterhead)) {
+                $fullPage = true;
+                $urlWebLetterhead = 'url('.api_get_path(WEB_CSS_PATH).$customLetterhead.')';
+            }
+
+            if($fullPage){
+                $pdf->pdf->SetDisplayMode('fullpage');
+                $pdf->pdf->SetDefaultBodyCSS('background',$urlWebLetterhead);
+                $pdf->pdf->SetDefaultBodyCSS('background-image-resize','6');
+            }
+
+            @$pdf->content_to_pdf($content,
+                $css = '',
+                $pdf_name = '',
+                $course_code = null,
+                $outputMode = 'D',
+                $saveInFile = false,
+                $fileToSave = null,
+                $returnHtml = false,
+                $addDefaultCss = true,
+                $completeHeader = false
+            );
+
+
+        } catch (MpdfException $e) {
+            error_log($e);
+        }
+        exit;
+        break;
     case 'export_one_session_row':
         $sessionToExport = isset($_GET['session_to_export']) ? (int) $_GET['session_to_export'] : 0;
         $exportList = Session::read('export_course_list');
@@ -1107,6 +1322,17 @@ if (empty($details)) {
                         ['action' => 'export_one_session_row', 'export' => 'xls', 'session_to_export' => $sId]
                     )
             );
+
+            if (!empty($sId)) {
+                $sessionAction .= Display::url(
+                    Display::return_icon('pdf.png', get_lang('ExportToPDF'), [], ICON_SIZE_MEDIUM),
+                    $currentUrl
+                    .'&'
+                    .http_build_query(
+                        ['action' => 'export_to_pdf', 'session_to_export' => $sId]
+                    )
+                );
+            }
             echo $sessionAction;
         } else {
             echo "<tr><td colspan='5'>".get_lang('NoCourse')."</td></tr>";

+ 20 - 3
main/template/default/learnpath/list.tpl

@@ -110,11 +110,17 @@
                                     <th>{{ "PublicationDate"|get_lang }}</th>
                                     <th>{{ "ExpirationDate"|get_lang }}</th>
                                     <th>{{ "Progress"|get_lang }}</th>
+                                    {% if allow_min_time %}
+                                        <th>{{ "TimeSpentTimeRequired"|get_lang }}</th>
+                                    {% endif %}
                                     <th>{{ "AuthoringOptions"|get_lang }}</th>
                                 {% else %}
                                     {% if not is_invitee %}
                                         <th>{{ "Progress"|get_lang }}</th>
                                     {% endif %}
+                                    {% if allow_min_time %}
+                                        <th>{{ "TimeSpentTimeRequired"|get_lang }}</th>
+                                    {% endif %}
                                     <th>{{ "Actions"|get_lang }}</th>
                                 {% endif %}
                             </tr>
@@ -142,17 +148,28 @@
                                         <td>
                                             {{ row.dsp_progress }}
                                         </td>
+                                        {% if allow_min_time %}
+                                            <td>
+                                            {% if row.info_time_prerequisite %}
+                                                {{ row.info_time_prerequisite }}
+                                            {% endif %}
+                                            </td>
+                                        {% endif %}
                                     {% else %}
                                         {% if not is_invitee %}
                                             <td>
                                                 {{ row.dsp_progress }}
                                             </td>
                                         {% endif %}
+                                        {% if allow_min_time %}
+                                            <td>
+                                                {% if row.info_time_prerequisite %}
+                                                    {{ row.info_time_prerequisite }}
+                                                {% endif %}
+                                            </td>
+                                        {% endif %}
                                     {% endif %}
                                     <td>
-                                        {% if row.info_time_prerequisite %}
-                                            {{ row.info_time_prerequisite }}
-                                        {% endif %}
                                         {{ row.action_build }}
                                         {{ row.action_edit }}
                                         {{ row.action_visible }}

+ 39 - 20
main/template/default/learnpath/view.tpl

@@ -64,29 +64,48 @@
                                 }, 1000);
                             })
                         </script>
-                        <b>
-                            {{ "TimeSpentInLp"|get_lang|format(lp_accumulate_work_time) }}
-                        </b>
-                        <div id="progress_bar">
-                            <div class="progress">
-                                <div id="progress_bar_value2"
-                                     class="progress-bar progress-bar-success"
-                                     role="progressbar" aria-valuenow="50"
-                                     aria-valuemin="0"
-                                     aria-valuemax="{{ time_progress_value }}"
-                                     style="width: {{ time_progress_perc }};"
-                                >
-                                    {{ time_progress_perc }}
+                        <div class="row">
+                            <div class="col-xs-4">
+                                <b>
+                                    {{ "ProgressSpentInLp"|get_lang|format(lp_accumulate_work_time) }}
+                                </b>
+                            </div>
+                            <div class="col-xs-8">
+                                <div id="progress_bar">
+                                    <div class="progress">
+                                        <div id="progress_bar_value2"
+                                             class="progress-bar progress-bar-success"
+                                             role="progressbar" aria-valuenow="50"
+                                             aria-valuemin="0"
+                                             aria-valuemax="{{ time_progress_value }}"
+                                             style="width: {{ time_progress_perc }};"
+                                        >
+                                            {{ time_progress_perc }}
+                                        </div>
+                                    </div>
                                 </div>
                             </div>
                         </div>
-                        <div id="timer">
-                            <div class="container">
-                                <div id="hour">{{ hour }}</div>
-                                <div class="divider">:</div>
-                                <div id="minute">{{ minute }}</div>
-                                <div class="divider">:</div>
-                                <div id="second">{{ second }}</div>
+
+                        <div class="row">
+                            <div class="col-xs-4">
+                                <b>
+                                    {{ "TimeSpentInLp"|get_lang|format(lp_accumulate_work_time) }}
+                                </b>
+                            </div>
+                            <div class="col-xs-8">
+                                <div id="timer">
+                                    <div class="container">
+                                        <div id="hour">{{ hour }}</div>
+                                        <div class="divider">:</div>
+                                        <div id="minute">{{ minute }}</div>
+                                        <div class="divider">:</div>
+                                        <div id="second">{{ second }}</div>
+
+                                        <div id="slash"> / </div>
+                                        <div>{{ hour_min }}</div>
+                                    </div>
+                                </div>
                             </div>
                         </div>
                         {% endset %}

+ 18 - 0
main/template/default/my_space/pdf_export_student.tpl

@@ -0,0 +1,18 @@
+<div style="position: absolute; top: 30px; right: 50px;">
+    <img src="{{ _p.web_css_theme }}images/header-logo.png" width="200">
+</div>
+<h1  style="color:#ffffff; background-color:#084B8A;font-size: 22px; text-align: center; font-weight: bold; padding: 5px 10px;">
+    {{ title }}
+</h1>
+<div style="font-weight: bold; padding-bottom: 20px;">
+    {{ student }}
+</div>
+<div style="background: #f1f6ff;">
+    {{ table_progress }}
+</div>
+<div style="padding-bottom: 20px; margin-top: 20px;">
+    {{ subtitle }}
+</div>
+<div style="background: #f1f6ff;">
+    {{ table_course }}
+</div>

+ 2 - 2
main/ticket/categories.php

@@ -129,8 +129,8 @@ switch ($action) {
             $values = $form->getSubmitValues();
 
             $params = [
-                'name' => Database::escape_string($values['name']),
-                'description' => Database::escape_string($values['description']),
+                'name' => $values['name'],
+                'description' => $values['description'],
                 'sys_lastedit_datetime' => api_get_utc_datetime(),
                 'sys_lastedit_user_id' => api_get_user_id(),
             ];

+ 1 - 1
main/ticket/ticket_details.php

@@ -318,7 +318,7 @@ if ($ticket['ticket']['status_id'] != TicketManager::STATUS_FORWARDED &&
                 $file_attachments,
                 $user_id,
                 'NOL',
-                Database::escape_string($message)
+                $message
             );
 
             TicketManager::sendNotification(