diff --git a/public/main/inc/lib/exercise.lib.php b/public/main/inc/lib/exercise.lib.php index a6d9d2a1df..0995bd19c5 100644 --- a/public/main/inc/lib/exercise.lib.php +++ b/public/main/inc/lib/exercise.lib.php @@ -65,7 +65,9 @@ class ExerciseLib return false; } - if (EXERCISE_FEEDBACK_TYPE_END != $exercise->getFeedbackType()) { + $questionRequireAuth = WhispeakAuthPlugin::questionRequireAuthentify($questionId); + + if ($exercise->getFeedbackType() != EXERCISE_FEEDBACK_TYPE_END) { $show_comment = false; } @@ -94,7 +96,14 @@ class ExerciseLib } echo $titleToDisplay; } - if (!empty($questionDescription) && READING_COMPREHENSION != $answerType) { + + if ($questionRequireAuth) { + WhispeakAuthPlugin::quizQuestionAuthentify($questionId, $exercise); + + return false; + } + + if (!empty($questionDescription) && $answerType != READING_COMPREHENSION) { echo Display::div( $questionDescription, ['class' => 'question_description'] @@ -128,10 +137,12 @@ class ExerciseLib if (DRAGGABLE == $answerType) { $isVertical = 'v' == $objQuestionTmp->extra; $s .= ' -
-
- +
+
+ "; $counterAnswer = 1; $s .= $isVertical ? '' : '
'; for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { @@ -1378,7 +1392,7 @@ HTML; } $s .= $isVertical ? '' : '
'; // row - $s .= ''; // col-md-12 ui-widget ui-helper-clearfix +// $s .= ''; } if (in_array($answerType, [MATCHING, MATCHING_DRAGGABLE])) { @@ -1474,6 +1488,13 @@ HTML; } echo $objQuestionTmp->getTitleToDisplay($current_item); } + //@todo I need to the get the feedback type + if ($questionRequireAuth) { + WhispeakAuthPlugin::quizQuestionAuthentify($questionId, $exercise); + + return false; + } + //@todo I need to the get the feedback type echo << @@ -1532,6 +1553,13 @@ HOTSPOT; } echo $objQuestionTmp->getTitleToDisplay($current_item); } + + if ($questionRequireAuth) { + WhispeakAuthPlugin::quizQuestionAuthentify($questionId, $exercise); + + return false; + } + echo '
@@ -4512,7 +4540,6 @@ EOT; ); } else { $pluginEvaluation = QuestionOptionsEvaluationPlugin::create(); - if ('true' === $pluginEvaluation->get(QuestionOptionsEvaluationPlugin::SETTING_ENABLE)) { $formula = $pluginEvaluation->getFormulaForExercise($objExercise->selectId()); diff --git a/public/main/inc/lib/fileUpload.lib.php b/public/main/inc/lib/fileUpload.lib.php index 52a0b6ac77..affe152878 100644 --- a/public/main/inc/lib/fileUpload.lib.php +++ b/public/main/inc/lib/fileUpload.lib.php @@ -1071,6 +1071,26 @@ function clean_up_files_in_zip($p_event, &$p_header) return 1; } +function cleanZipFilesNoRename($p_event, &$p_header) +{ + $originalStoredFileName = $p_header['stored_filename']; + $baseName = basename($originalStoredFileName); + // Skip files + $skipFiles = [ + '__MACOSX', + '.Thumbs.db', + 'Thumbs.db', + ]; + + if (in_array($baseName, $skipFiles)) { + return 0; + } + $modifiedStoredFileName = clean_up_path($originalStoredFileName, false); + $p_header['filename'] = str_replace($originalStoredFileName, $modifiedStoredFileName, $p_header['filename']); + + return 1; +} + /** * Allow .htaccess file. * @@ -1110,21 +1130,25 @@ function cleanZipFilesAllowHtaccess($p_event, &$p_header) * by eliminating dangerous file names and cleaning them. * * @param string $path + * @param bool $replaceName * * @return string * * @see disable_dangerous_file() * @see api_replace_dangerous_char() */ -function clean_up_path($path) +function clean_up_path($path, $replaceName = true) { // Split the path in folders and files $path_array = explode('/', $path); // Clean up every folder and filename in the path foreach ($path_array as $key => &$val) { // We don't want to lose the dots in ././folder/file (cfr. zipfile) - if ('.' != $val) { - $val = disable_dangerous_file(api_replace_dangerous_char($val)); + if ($val != '.') { + if ($replaceName) { + $val = api_replace_dangerous_char($val); + } + $val = disable_dangerous_file($val); } } // Join the "cleaned" path (modified in-place as passed by reference) diff --git a/public/main/inc/lib/lp_item.lib.php b/public/main/inc/lib/lp_item.lib.php index 90be7df51c..3450643c3f 100644 --- a/public/main/inc/lib/lp_item.lib.php +++ b/public/main/inc/lib/lp_item.lib.php @@ -109,4 +109,40 @@ class LpItem Database::query($sql); } } + + /** + * Create extra field for learning path item. + * + * @param string $variable + * @param int $fieldType + * @param string $displayText + * @param string|null $default Optional. + * @param bool $changeable Optional. + * @param bool $visibleToSelf Optional. + * @param bool $visibleToOthers Optional. + * + * @return bool|int + */ + public static function createExtraField( + $variable, + $fieldType, + $displayText, + $default = null, + $changeable = false, + $visibleToSelf = false, + $visibleToOthers = false + ) { + $extraField = new ExtraField('lp_item'); + $params = [ + 'variable' => $variable, + 'field_type' => $fieldType, + 'display_text' => $displayText, + 'default_value' => $default, + 'changeable' => $changeable, + 'visible_to_self' => $visibleToSelf, + 'visible_to_others' => $visibleToOthers, + ]; + + return $extraField->save($params); + } } diff --git a/public/main/inc/lib/message.lib.php b/public/main/inc/lib/message.lib.php index bb26f5f4d7..6cdc9c0559 100644 --- a/public/main/inc/lib/message.lib.php +++ b/public/main/inc/lib/message.lib.php @@ -453,11 +453,12 @@ class MessageManager * @param array $smsParameters * @param bool $checkCurrentAudioId * @param bool $forceTitleWhenSendingEmail force the use of $title as subject instead of "You have a new message" + * @param int $status Message status * * @return bool */ public static function send_message( - $receiver_user_id, + $receiverUserId, $subject, $content, array $attachments = [], @@ -474,22 +475,75 @@ class MessageManager $forceTitleWhenSendingEmail = false, $status = 0 ) { - $table = Database::get_main_table(TABLE_MESSAGE); $group_id = (int) $group_id; - $receiver_user_id = (int) $receiver_user_id; + $receiverUserId = (int) $receiverUserId; $parent_id = (int) $parent_id; $editMessageId = (int) $editMessageId; $topic_id = (int) $topic_id; - $status = empty($status) ? MESSAGE_STATUS_UNREAD : (int) $status; - if (!empty($receiver_user_id)) { - $receiverUserInfo = api_get_user_info($receiver_user_id); + $sendEmail = true; + if (!empty($receiverUserId)) { + $receiverUserInfo = api_get_user_info($receiverUserId); + if (empty($receiverUserInfo)) { + return false; + } // Disabling messages for inactive users. if (0 == $receiverUserInfo['active']) { return false; } + + // Disabling messages depending the pausetraining plugin. + $allowPauseFormation = + 'true' === api_get_plugin_setting('pausetraining', 'tool_enable') && + 'true' === api_get_plugin_setting('pausetraining', 'allow_users_to_edit_pause_formation'); + + if ($allowPauseFormation) { + $extraFieldValue = new ExtraFieldValue('user'); + $disableEmails = $extraFieldValue->get_values_by_handler_and_field_variable( + $receiverUserId, + 'disable_emails' + ); + + // User doesn't want email notifications but chamilo inbox still available. + if (!empty($disableEmails) && + isset($disableEmails['value']) && 1 === (int) $disableEmails['value'] + ) { + $sendEmail = false; + } + + if ($sendEmail) { + // Check if user pause his formation. + $pause = $extraFieldValue->get_values_by_handler_and_field_variable( + $receiverUserId, + 'pause_formation' + ); + if (!empty($pause) && isset($pause['value']) && 1 === (int) $pause['value']) { + $startDate = $extraFieldValue->get_values_by_handler_and_field_variable( + $receiverUserInfo['user_id'], + 'start_pause_date' + ); + $endDate = $extraFieldValue->get_values_by_handler_and_field_variable( + $receiverUserInfo['user_id'], + 'end_pause_date' + ); + + if ( + !empty($startDate) && isset($startDate['value']) && !empty($startDate['value']) && + !empty($endDate) && isset($endDate['value']) && !empty($endDate['value']) + ) { + $now = time(); + $start = api_strtotime($startDate['value']); + $end = api_strtotime($endDate['value']); + + if ($now > $start && $now < $end) { + $sendEmail = false; + } + } + } + } + } } $user_sender_id = empty($sender_id) ? api_get_user_id() : (int) $sender_id; @@ -548,7 +602,7 @@ class MessageManager ); return false; - } elseif ($totalFileSize > intval(api_get_setting('message_max_upload_filesize'))) { + } elseif ($totalFileSize > (int) api_get_setting('message_max_upload_filesize')) { $warning = sprintf( get_lang('Files size exceeds'), format_file_size(api_get_setting('message_max_upload_filesize')) @@ -559,10 +613,10 @@ class MessageManager return false; } - // Just in case we replace the and \n and \n\r while saving in the DB - // $content = str_replace(array("\n", "\n\r"), '
', $content); $now = api_get_utc_datetime(); - if (!empty($receiver_user_id) || !empty($group_id)) { + $table = Database::get_main_table(TABLE_MESSAGE); + // Just in case we replace the and \n and \n\r while saving in the DB + if (!empty($receiverUserId) || !empty($group_id)) { // message for user friend //@todo it's possible to edit a message? yes, only for groups if (!empty($editMessageId)) { @@ -575,7 +629,7 @@ class MessageManager } else { $params = [ 'user_sender_id' => $user_sender_id, - 'user_receiver_id' => $receiver_user_id, + 'user_receiver_id' => $receiverUserId, 'msg_status' => $status, 'send_date' => $now, 'title' => $subject, @@ -616,7 +670,7 @@ class MessageManager $comment, $messageId, null, - $receiver_user_id, + $receiverUserId, $group_id ); } @@ -627,7 +681,7 @@ class MessageManager if (empty($group_id) && MESSAGE_STATUS_UNREAD == $status) { $params = [ 'user_sender_id' => $user_sender_id, - 'user_receiver_id' => $receiver_user_id, + 'user_receiver_id' => $receiverUserId, 'msg_status' => MESSAGE_STATUS_OUTBOX, 'send_date' => $now, 'title' => $subject, @@ -654,69 +708,70 @@ class MessageManager } } - // Load user settings. - $notification = new Notification(); - $sender_info = api_get_user_info($user_sender_id); + if ($sendEmail) { + $notification = new Notification(); + $sender_info = api_get_user_info($user_sender_id); - // add file attachment additional attributes - $attachmentAddedByMail = []; - foreach ($attachmentList as $attachment) { - $attachmentAddedByMail[] = [ - 'path' => $attachment['tmp_name'], - 'filename' => $attachment['name'], - ]; - } - - if (empty($group_id)) { - $type = Notification::NOTIFICATION_TYPE_MESSAGE; - if ($directMessage) { - $type = Notification::NOTIFICATION_TYPE_DIRECT_MESSAGE; + // add file attachment additional attributes + $attachmentAddedByMail = []; + foreach ($attachmentList as $attachment) { + $attachmentAddedByMail[] = [ + 'path' => $attachment['tmp_name'], + 'filename' => $attachment['name'], + ]; } - $notification->saveNotification( - $messageId, - $type, - [$receiver_user_id], - $subject, - $content, - $sender_info, - $attachmentAddedByMail, - $smsParameters, - $forceTitleWhenSendingEmail - ); - } else { - $usergroup = new UserGroup(); - $group_info = $usergroup->get($group_id); - $group_info['topic_id'] = $topic_id; - $group_info['msg_id'] = $messageId; - $user_list = $usergroup->get_users_by_group( - $group_id, - false, - [], - 0, - 1000 - ); + if (empty($group_id)) { + $type = Notification::NOTIFICATION_TYPE_MESSAGE; + if ($directMessage) { + $type = Notification::NOTIFICATION_TYPE_DIRECT_MESSAGE; + } + $notification->saveNotification( + $messageId, + $type, + [$receiverUserId], + $subject, + $content, + $sender_info, + $attachmentAddedByMail, + $smsParameters, + $forceTitleWhenSendingEmail + ); + } else { + $usergroup = new UserGroup(); + $group_info = $usergroup->get($group_id); + $group_info['topic_id'] = $topic_id; + $group_info['msg_id'] = $messageId; - // Adding more sense to the message group - $subject = sprintf(get_lang('There is a new message in group %s'), $group_info['name']); - $new_user_list = []; - foreach ($user_list as $user_data) { - $new_user_list[] = $user_data['id']; + $user_list = $usergroup->get_users_by_group( + $group_id, + false, + [], + 0, + 1000 + ); + + // Adding more sense to the message group + $subject = sprintf(get_lang('There is a new message in group %s'), $group_info['name']); + $new_user_list = []; + foreach ($user_list as $user_data) { + $new_user_list[] = $user_data['id']; + } + $group_info = [ + 'group_info' => $group_info, + 'user_info' => $sender_info, + ]; + $notification->saveNotification( + $messageId, + Notification::NOTIFICATION_TYPE_GROUP, + $new_user_list, + $subject, + $content, + $group_info, + $attachmentAddedByMail, + $smsParameters + ); } - $group_info = [ - 'group_info' => $group_info, - 'user_info' => $sender_info, - ]; - $notification->saveNotification( - $messageId, - Notification::NOTIFICATION_TYPE_GROUP, - $new_user_list, - $subject, - $content, - $group_info, - $attachmentAddedByMail, - $smsParameters - ); } return $messageId; @@ -726,7 +781,7 @@ class MessageManager } /** - * @param int $receiver_user_id + * @param int $receiverUserId * @param int $subject * @param string $message * @param int $sender_id @@ -739,7 +794,7 @@ class MessageManager * @return bool */ public static function send_message_simple( - $receiver_user_id, + $receiverUserId, $subject, $message, $sender_id = 0, @@ -758,7 +813,7 @@ class MessageManager $files = $attachmentList; } $result = self::send_message( - $receiver_user_id, + $receiverUserId, $subject, $message, $files, @@ -774,8 +829,8 @@ class MessageManager ); if ($sendCopyToDrhUsers) { - $userInfo = api_get_user_info($receiver_user_id); - $drhList = UserManager::getDrhListFromUser($receiver_user_id); + $userInfo = api_get_user_info($receiverUserId); + $drhList = UserManager::getDrhListFromUser($receiverUserId); if (!empty($drhList)) { foreach ($drhList as $drhInfo) { $message = sprintf( @@ -784,7 +839,7 @@ class MessageManager ).'
'.$message; self::send_message_simple( - $drhInfo['user_id'], + $drhInfo['id'], $subject, $message, $sender_id, @@ -798,47 +853,6 @@ class MessageManager return $result; } - /** - * Update parent ids for other receiver user from current message in groups. - * - * @author Christian Fasanando Flores - * - * @param int $parent_id - * @param int $receiver_user_id - * @param int $messageId - */ - public static function update_parent_ids_from_reply( - $parent_id, - $receiver_user_id, - $messageId - ) { - $table = Database::get_main_table(TABLE_MESSAGE); - $parent_id = intval($parent_id); - $receiver_user_id = intval($receiver_user_id); - $messageId = intval($messageId); - - // first get data from message id (parent) - $sql = "SELECT * FROM $table WHERE id = '$parent_id'"; - $rs_message = Database::query($sql); - $row_message = Database::fetch_array($rs_message); - - // get message id from data found early for other receiver user - $sql = "SELECT id FROM $table - WHERE - user_sender_id ='{$row_message['user_sender_id']}' AND - title='{$row_message['title']}' AND - content='{$row_message['content']}' AND - group_id='{$row_message['group_id']}' AND - user_receiver_id='$receiver_user_id'"; - $result = Database::query($sql); - $row = Database::fetch_array($result); - - // update parent_id for other user receiver - $sql = "UPDATE $table SET parent_id = ".$row['id']." - WHERE id = $messageId"; - Database::query($sql); - } - /** * @param int $user_receiver_id * @param int $id @@ -1080,7 +1094,9 @@ class MessageManager WHERE user_receiver_id = ".$user_id." AND id = '".$message_id."'"; - Database::query($sql); + $result = Database::query($sql); + + return Database::affected_rows($result) > 0; } /** @@ -1368,6 +1384,26 @@ class MessageManager return ''; } + /* get previous message */ + $query = "SELECT id FROM $table + WHERE + $userCondition + id < $messageId + order by id DESC limit 1 "; + $result = Database::query($query); + $rowPrevMessage = Database::fetch_array($result, 'ASSOC'); + $idPrevMessage = (int) isset($rowPrevMessage['id']) ? $rowPrevMessage['id'] : 0; + + /* get next message */ + $query = "SELECT id FROM $table + WHERE + $userCondition + id > $messageId + order by id ASC limit 1 "; + $result = Database::query($query); + $rowNextMessage = Database::fetch_array($result, 'ASSOC'); + $idNextMessage = (int) isset($rowNextMessage['id']) ? $rowNextMessage['id'] : 0; + $user_sender_id = $row['user_sender_id']; // get file attachments by message id @@ -1416,7 +1452,6 @@ class MessageManager switch ($type) { case self::MESSAGE_TYPE_INBOX: - //$message_content .= api_strtolower(get_lang('To')).' -'; $message_content .= ' '.api_strtolower(get_lang('To')).' '.get_lang('Me'); break; case self::MESSAGE_TYPE_OUTBOX: @@ -1475,12 +1510,18 @@ class MessageManager $message_content .= ''. Display::return_icon('message_reply.png', get_lang('Reply to this message')).'  '; $message_content .= ''. - Display::return_icon('delete.png', get_lang('Delete message')).' '; + Display::return_icon('delete.png', get_lang('DeleteMessage')).' '; + if ($idPrevMessage != 0) { + $message_content .= ''.Display::return_icon('icons/22/back.png', get_lang('ScormPrevious')).'  '; + } + if ($idNextMessage != 0) { + $message_content .= ''.Display::return_icon('icons/22/move.png', get_lang('ScormNext')).'  '; + } break; } $message_content .= '
- + '; @@ -2162,7 +2203,6 @@ class MessageManager 20, 'DESC' ); - $table->setDataFunctionParams( ['keyword' => $keyword, 'type' => $type, 'actions' => $actions] ); diff --git a/public/main/inc/lib/online.inc.php b/public/main/inc/lib/online.inc.php index a87d44ed29..5843e44a2e 100644 --- a/public/main/inc/lib/online.inc.php +++ b/public/main/inc/lib/online.inc.php @@ -13,8 +13,9 @@ use ChamiloSession as Session; */ /** - * Insert a login reference for the current user into the track_e_online stats table. - * This table keeps trace of the last login. Nothing else matters (we don't keep traces of anything older). + * Insert a login reference for the current user into the track_e_online stats + * table. This table keeps trace of the last login. Nothing else matters (we + * don't keep traces of anything older). * * @param int user id */ @@ -35,16 +36,39 @@ function LoginCheck($uid) $access_url_id = api_get_current_access_url_id(); } $session_id = api_get_session_id(); - // if the $_course array exists this means we are in a course and we have to store this in the who's online table also - // to have the x users in this course feature working - if (is_array($_course) && count($_course) > 0 && !empty($_course['id'])) { - $query = "REPLACE INTO ".$online_table." (login_id,login_user_id,login_date,user_ip, c_id, session_id, access_url_id) - VALUES ($uid,$uid,'$login_date','$user_ip', '".$_course['real_id']."' , '$session_id' , '$access_url_id' )"; + $cid = 0; + if (is_array($_course) && count($_course) > 0 && !empty($_course['real_id'])) { + $cid = intval($_course['real_id']); + } + $query = "SELECT login_id FROM $online_table WHERE login_user_id = $uid"; + $resLogin = Database::query($query); + if (Database::num_rows($resLogin) > 0) { + $query = "UPDATE $online_table SET + login_date = '$login_date', + user_ip = '$user_ip', + c_id = $cid, + session_id = $session_id, + access_url_id = $access_url_id + WHERE login_user_id = $uid"; + Database::query($query); } else { - $query = "REPLACE INTO ".$online_table." (login_id,login_user_id,login_date,user_ip, c_id, session_id, access_url_id) - VALUES ($uid,$uid,'$login_date','$user_ip', 0, '$session_id', '$access_url_id')"; + $query = "INSERT $online_table ( + login_user_id, + login_date, + user_ip, + c_id, + session_id, + access_url_id + ) values ( + $uid, + '$login_date', + '$user_ip', + $cid, + $session_id, + $access_url_id + )"; + Database::query($query); } - Database::query($query); } } diff --git a/public/main/inc/lib/security.lib.php b/public/main/inc/lib/security.lib.php index 3946d0a63d..8bd7fa1935 100644 --- a/public/main/inc/lib/security.lib.php +++ b/public/main/inc/lib/security.lib.php @@ -1,4 +1,5 @@ getSubmitValue('protect_token'); + + if (!empty($sessionToken) && !empty($token) && $sessionToken === $token) { + return true; + } + return false; default: if (!empty($sessionToken) && isset($request_type) && $sessionToken === $request_type) { diff --git a/public/main/inc/lib/social.lib.php b/public/main/inc/lib/social.lib.php index 3627d12030..9acd284c70 100644 --- a/public/main/inc/lib/social.lib.php +++ b/public/main/inc/lib/social.lib.php @@ -787,9 +787,7 @@ class SocialManager extends UserManager ); } - $skillBlock = $template->get_template('social/avatar_block.tpl'); - - return $template->fetch($skillBlock); + return $template->fetch($template->get_template('social/avatar_block.tpl')); } /** diff --git a/public/main/inc/lib/sortable_table.class.php b/public/main/inc/lib/sortable_table.class.php index 50e689b694..573b2d64ed 100644 --- a/public/main/inc/lib/sortable_table.class.php +++ b/public/main/inc/lib/sortable_table.class.php @@ -909,7 +909,7 @@ class SortableTable extends HTML_Table { $param_string_parts = []; if (is_array($this->additional_parameters) && count($this->additional_parameters) > 0) { - foreach ($this->additional_parameters as $key => &$value) { + foreach ($this->additional_parameters as $key => $value) { $param_string_parts[] = urlencode($key).'='.urlencode($value); } } @@ -917,7 +917,6 @@ class SortableTable extends HTML_Table foreach ($this->other_tables as $index => &$tablename) { $param = []; if (isset($_GET[$tablename.'_direction'])) { - //$param[$tablename.'_direction'] = $_GET[$tablename.'_direction']; $my_get_direction = $_GET[$tablename.'_direction']; if (!in_array($my_get_direction, ['ASC', 'DESC'])) { $param[$tablename.'_direction'] = 'ASC'; diff --git a/public/main/inc/lib/statistics.lib.php b/public/main/inc/lib/statistics.lib.php index 92038af5ed..cb979587ed 100644 --- a/public/main/inc/lib/statistics.lib.php +++ b/public/main/inc/lib/statistics.lib.php @@ -247,7 +247,6 @@ class Statistics default_value_type LIKE '%".$keyword."%' OR default_value LIKE '%".$keyword."%') "; } - $res = Database::query($sql); $obj = Database::fetch_object($res); @@ -278,9 +277,9 @@ class Statistics $table_user = Database::get_main_table(TABLE_MAIN_USER); $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER); $urlId = api_get_current_access_url_id(); - $column = intval($column); - $from = intval($from); - $numberOfItems = intval($numberOfItems); + $column = (int) $column; + $from = (int) $from; + $numberOfItems = (int) $numberOfItems; $direction = strtoupper($direction); if (!in_array($direction, ['ASC', 'DESC'])) { @@ -364,6 +363,12 @@ class Statistics $row['default_date'] = '-'; } + if (!empty($row[7])) { + $row[7] = api_get_local_time($row[7]); + } else { + $row[7] = '-'; + } + if (!empty($row[5])) { // Course if (!empty($row[3])) { @@ -458,6 +463,8 @@ class Statistics * @param array $stats * @param bool $showTotal * @param bool $isFileSize + * + * @return string HTML table */ public static function printStats( $title, @@ -466,8 +473,7 @@ class Statistics $isFileSize = false ) { $total = 0; - $data = self::rescale($stats); - echo ' + $content = '
'; $i = 0; foreach ($stats as $subtitle => $number) { @@ -482,14 +488,14 @@ class Statistics } $percentage = ($total > 0 ? number_format(100 * $number / $total, 1, ',', '.') : '0'); - echo ' - - - '; + $content .= ' + + + '; if ($showTotal) { - echo ''; + $content .= ''; } - echo ''; + $content .= ''; $i++; } if ($showTotal) { @@ -498,9 +504,11 @@ class Statistics } else { $total_label = self::makeSizeString($total); } - echo ''; + $content .= ''; } - echo '
'.$title.'
'.$subtitle.''.Display::bar_progress($percentage, false).''.$number_label.'
'.$subtitle.''.Display::bar_progress($percentage, false).''.$number_label.' '.$percentage.'% '.$percentage.'%
'.get_lang('Total').': '.$total_label.'
'.get_lang('Total').': '.$total_label.'
'; + $content .= ''; + + return $content; } /** @@ -567,16 +575,17 @@ class Statistics break; } + $content = ''; if ($sql_last_x) { $res_last_x = Database::query($sql_last_x); $result_last_x = []; while ($obj = Database::fetch_object($res_last_x)) { - $stat_date = ('day' === $type) ? $periodCollection[$obj->stat_date] : $obj->stat_date; + $stat_date = ($type === 'day') ? $periodCollection[$obj->stat_date] : $obj->stat_date; $result_last_x[$stat_date] = $obj->number_of_logins; } - self::printStats(get_lang('Last logins').' ('.$period.')', $result_last_x, true); + $content .= self::printStats(get_lang('LastLogins').' ('.$period.')', $result_last_x, true); flush(); //flush web request at this point to see something already while the full data set is loading - echo '
'; + $content .= '
'; } $res = Database::query($sql); $result = []; @@ -594,16 +603,23 @@ class Statistics } $result[$stat_date] = $obj->number_of_logins; } - self::printStats(get_lang('All logins').' ('.$period.')', $result, true); + $content .= self::printStats(get_lang('AllLogins').' ('.$period.')', $result, true); + + return $content; } /** * Print the number of recent logins. * * @param bool $distinct whether to only give distinct users stats, or *all* logins - * @param int $sessionDuration + * @param int $sessionDuration Number of minutes a session must have lasted at a minimum to be taken into account + * @param array $periods List of number of days we want to query (default: [1, 7, 31] for last 1 day, last 7 days, last 31 days) + * + * @throws Exception + * + * @return string HTML table */ - public static function printRecentLoginStats($distinct = false, $sessionDuration = 0) + public static function printRecentLoginStats($distinct = false, $sessionDuration = 0, $periods = []) { $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN); $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER); @@ -621,11 +637,13 @@ class Statistics $field = 'DISTINCT(login_user_id)'; } - $days = [1, 7, 15, 31]; + if (empty($periods)) { + $periods = [1, 7, 31]; + } $sqlList = []; - $sessionDuration = (int) $sessionDuration; - foreach ($days as $day) { + $sessionDuration = (int) $sessionDuration * 60; // convert from minutes to seconds + foreach ($periods as $day) { $date = new DateTime($now); $startDate = $date->format('Y-m-d').' 00:00:00'; $endDate = $date->format('Y-m-d').' 23:59:59'; @@ -642,20 +660,27 @@ class Statistics if (1 == $day) { $label = get_lang('Today'); } - $label .= "
$localDate - $localEndDate"; + $label .= " [$localDate - $localEndDate]"; $sql = "SELECT count($field) AS number FROM $table $table_url - WHERE - UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration AND - login_date BETWEEN '$startDate' AND '$endDate' + WHERE "; + if ($sessionDuration == 0) { + $sql .= " logout_date != login_date AND "; + } else { + $sql .= " UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration AND "; + } + $sql .= "login_date BETWEEN '$startDate' AND '$endDate' $where_url"; $sqlList[$label] = $sql; } $sql = "SELECT count($field) AS number - FROM $table $table_url - WHERE UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration $where_url - "; + FROM $table $table_url "; + if ($sessionDuration == 0) { + $sql .= " WHERE logout_date != login_date $where_url"; + } else { + $sql .= " WHERE UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration $where_url"; + } $sqlList[get_lang('Total')] = $sql; $totalLogin = []; foreach ($sqlList as $label => $query) { @@ -663,55 +688,25 @@ class Statistics $obj = Database::fetch_object($res); $totalLogin[$label] = $obj->number; } + if ($distinct) { - self::printStats(get_lang('Distinct users logins'), $totalLogin, false); + $content = self::printStats(get_lang('DistinctUsersLogins'), $totalLogin, false); } else { - self::printStats(get_lang('Logins'), $totalLogin, false); - } + $content = self::printStats(get_lang('Logins'), $totalLogin, false); } - public static function getLoginCount($startDate, $endDate) - { - $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN); - $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER); - $urlId = api_get_current_access_url_id(); - $table_url = ''; - $where_url = ''; - if (api_is_multiple_url_enabled()) { - $table_url = ", $access_url_rel_user_table"; - $where_url = " AND login_user_id=user_id AND access_url_id='".$urlId."'"; - } - $startDate = Database::escape_string($startDate); - $endDate = Database::escape_string($endDate); - - $sql = " - SELECT count(logins) count FROM ( - SELECT count(login_user_id) AS logins - FROM $table $table_url - WHERE - login_date BETWEEN '$startDate' AND '$endDate' - $where_url - GROUP BY login_user_id - ) as t - "; - - $res = Database::query($sql); - $totalLogin = 0; - $row = Database::fetch_array($res, 'ASSOC'); - if ($row) { - $totalLogin = $row['count']; - } - - return $totalLogin; + return $content; } /** - * get the number of recent logins. + * Get the number of recent logins. * * @param bool $distinct Whether to only give distinct users stats, or *all* logins - * @param int $sessionDuration + * @param int $sessionDuration Number of minutes a session must have lasted at a minimum to be taken into account * @param bool $completeMissingDays Whether to fill the daily gaps (if any) when getting a list of logins * + * @throws Exception + * * @return array */ public static function getRecentLoginStats($distinct = false, $sessionDuration = 0, $completeMissingDays = true) @@ -728,7 +723,7 @@ class Statistics $now = api_get_utc_datetime(); $date = new DateTime($now); - $date->sub(new DateInterval('P15D')); + $date->sub(new DateInterval('P31D')); $newDate = $date->format('Y-m-d h:i:s'); $totalLogin = self::buildDatesArray($newDate, $now, true); @@ -736,13 +731,17 @@ class Statistics if ($distinct) { $field = 'DISTINCT(login_user_id)'; } - $sessionDuration = (int) $sessionDuration; + $sessionDuration = (int) $sessionDuration * 60; //Convert from minutes to seconds $sql = "SELECT count($field) AS number, date(login_date) as login_date FROM $table $table_url - WHERE - UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration AND - login_date >= '$newDate' $where_url + WHERE "; + if ($sessionDuration == 0) { + $sql .= " logout_date != login_date AND "; + } else { + $sql .= " UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration AND "; + } + $sql .= " login_date >= '$newDate' $where_url GROUP BY date(login_date)"; $res = Database::query($sql); @@ -812,13 +811,15 @@ class Statistics * Show some stats about the accesses to the different course tools. * * @param array $result If defined, this serves as data. Otherwise, will get the data from getToolsStats() + * + * @return string HTML table */ public static function printToolStats($result = null) { if (empty($result)) { $result = self::getToolsStats(); } - self::printStats(get_lang('Tools access'), $result, true); + return self::printStats(get_lang('Tools access'), $result, true); } /** @@ -876,7 +877,7 @@ class Statistics $result[get_lang('No')] = $count1->n - $count2->n; $result[get_lang('Yes')] = $count2->n; // #users with picture - self::printStats(get_lang('Number of users').' ('.get_lang('Picture').')', $result, true); + return self::printStats(get_lang('Number of users').' ('.get_lang('Picture').')', $result, true); } /** @@ -884,7 +885,7 @@ class Statistics */ public static function printActivitiesStats() { - echo '

'.get_lang('Important activities').'

'; + $content = '

'.get_lang('Important activities').'

'; // Create a search-box $form = new FormValidator( 'search_simple', @@ -901,9 +902,9 @@ class Statistics $form->addHidden('activities_column', '4'); $form->addElement('text', 'keyword', get_lang('Keyword')); $form->addButtonSearch(get_lang('Search'), 'submit'); - echo '
'; - $form->display(); - echo '
'; + $content .= '
'; + $content .= $form->returnForm(); + $content .= '
'; $table = new SortableTable( 'activities', @@ -929,7 +930,9 @@ class Statistics $table->set_header(5, get_lang('Username')); $table->set_header(6, get_lang('IP address')); $table->set_header(7, get_lang('Date')); - $table->display(); + $content .= $table->return_table(); + + return $content; } /** @@ -963,7 +966,8 @@ class Statistics $defaults['date_diff'] = Security::remove_XSS($_GET['date_diff']); } $form->setDefaults($defaults); - $form->display(); + $content = $form->returnForm(); + $values = $form->exportValues(); $date_diff = $values['date_diff']; $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS); @@ -985,7 +989,7 @@ class Statistics $from = ($page_nr - 1) * $per_page; $sql .= ' LIMIT '.$from.','.$per_page; - echo '

'.get_lang('Latest access').' >= '.$date_diff.' '.get_lang('days').'

'; + $content .= '

'.get_lang('Latest access').' >= '.$date_diff.' '.get_lang('days').'

'; $res = Database::query($sql); if (Database::num_rows($res) > 0) { $courses = []; @@ -999,8 +1003,10 @@ class Statistics } $parameters['date_diff'] = $date_diff; $parameters['report'] = 'courselastvisit'; - $table_header[] = [get_lang("Code"), true]; - $table_header[] = [get_lang("Latest access"), true]; + $table_header[] = [get_lang("CourseCode"), true]; + $table_header[] = [get_lang("LastAccess"), true]; + + ob_start(); Display:: display_sortable_table( $table_header, $courses, @@ -1008,9 +1014,13 @@ class Statistics [], $parameters ); + $content .= ob_get_contents(); + ob_end_clean(); } else { - echo get_lang('No search results'); + $content = get_lang('NoSearchResults'); } + + return $content; } /** @@ -1149,7 +1159,8 @@ class Statistics $r = $total - $obj->number; $totalLogin[$index] = $r < 0 ? 0 : $r; } - self::printStats( + + return self::printStats( get_lang('Not logged in for some time'), $totalLogin, false @@ -1216,11 +1227,14 @@ class Statistics success: function(data) { Chart.defaults.global.responsive = true; var ctx = document.getElementById("'.$elementId.'").getContext("2d"); - var myLoginChart = new Chart(ctx, { + var chart = new Chart(ctx, { type: "'.$type.'", data: data, options: {'.$options.'} }); + var title = chart.options.title.text; + $("#'.$elementId.'_title").html(title); + $("#'.$elementId.'_table").html(data.table); } }); }); @@ -1229,6 +1243,74 @@ class Statistics return $chartCode; } + public static function getJSChartTemplateWithData($data, $type = 'pie', $options = '', $elementId = 'canvas') + { + $data = json_encode($data); + $chartCode = ' + '; + + return $chartCode; + } + + public static function buildJsChartData($all, $chartName) + { + $list = []; + $palette = ChamiloApi::getColorPalette(true, true); + foreach ($all as $tick => $tock) { + $list['labels'][] = $tick; + } + + $list['datasets'][0]['label'] = $chartName; + $list['datasets'][0]['borderColor'] = 'rgba(255,255,255,1)'; + + $i = 0; + foreach ($all as $tick => $tock) { + $j = $i % count($palette); + $list['datasets'][0]['data'][] = $tock; + $list['datasets'][0]['backgroundColor'][] = $palette[$j]; + $i++; + } + + $scoreDisplay = ScoreDisplay::instance(); + $table = new HTML_Table(['class' => 'data_table']); + $headers = [ + get_lang('Name'), + get_lang('Count'), + get_lang('Percentage'), + ]; + $row = 0; + $column = 0; + foreach ($headers as $header) { + $table->setHeaderContents($row, $column, $header); + $column++; + } + + $total = 0; + foreach ($all as $name => $value) { + $total += $value; + } + $row++; + foreach ($all as $name => $value) { + $table->setCellContents($row, 0, $name); + $table->setCellContents($row, 1, $value); + $table->setCellContents($row, 2, $scoreDisplay->display_score([$value, $total], SCORE_PERCENT)); + $row++; + } + $table = Display::page_subheader2($chartName).$table->toHtml(); + + return ['chart' => $list, 'table' => $table]; + } + /** * Display the Logins By Date report and allow export its result to XLS. */ @@ -1248,15 +1330,12 @@ class Statistics } Export::arrayToXls($data); - exit; } - echo Display::page_header(get_lang('Logins by date')); + $content = Display::page_header(get_lang('Logins by date')); $actions = ''; - $content = ''; - $form = new FormValidator('frm_logins_by_date', 'get'); $form->addDateRangePicker( 'daterange', @@ -1304,13 +1383,48 @@ class Statistics $content = $table->toHtml(); } - $form->display(); + $content .= $form->returnForm(); if (!empty($actions)) { - echo Display::toolbarAction('logins_by_date_toolbar', [$actions]); + $content .= Display::toolbarAction('logins_by_date_toolbar', [$actions]); + } + + return $content; + } + + public static function getBossTable($bossId) + { + $students = UserManager::getUsersFollowedByStudentBoss($bossId); + + if (!empty($students)) { + $table = new HTML_Table(['class' => 'table table-responsive', 'id' => 'table_'.$bossId]); + $headers = [ + get_lang('Name'), + //get_lang('LastName'), + ]; + $row = 0; + $column = 0; + foreach ($headers as $header) { + $table->setHeaderContents($row, $column, $header); + $column++; + } + $row++; + foreach ($students as $student) { + $column = 0; + $content = api_get_person_name($student['firstname'], $student['lastname']).''; + $content = '
'.$content.'
'; + $table->setCellContents( + $row, + $column++, + $content + ); + $row++; + } + + return $table->toHtml(); } - echo $content; + return '
'; } /** diff --git a/public/main/inc/lib/thematic.lib.php b/public/main/inc/lib/thematic.lib.php index 3606f15080..ac0deb1ea3 100644 --- a/public/main/inc/lib/thematic.lib.php +++ b/public/main/inc/lib/thematic.lib.php @@ -687,13 +687,15 @@ class Thematic * @param int $thematic_advance_id Thematic advance id (optional), get data by thematic advance list * @param string $course_code Course code (optional) * @param bool $force_session_id Force to have a session id + * @param bool $withLocalTime Force start_date to local time * * @return array $data */ public function get_thematic_advance_list( $thematic_advance_id = null, $course_code = null, - $force_session_id = false + $force_session_id = false, + $withLocalTime = false ) { $course_info = api_get_course_info($course_code); $tbl_thematic_advance = Database::get_course_table(TABLE_THEMATIC_ADVANCE); @@ -741,6 +743,9 @@ class Thematic // group all data group by thematic id $tmp = []; while ($row = Database::fetch_array($res, 'ASSOC')) { + if ($withLocalTime == true) { + $row['start_date'] = api_get_local_time($row['start_date']); + } $tmp[] = $row['thematic_id']; if (in_array($row['thematic_id'], $tmp)) { if ($force_session_id) { diff --git a/public/main/inc/lib/tracking.lib.php b/public/main/inc/lib/tracking.lib.php index 1c522f26c4..300f6e3f44 100644 --- a/public/main/inc/lib/tracking.lib.php +++ b/public/main/inc/lib/tracking.lib.php @@ -531,7 +531,7 @@ class Tracking $lesson_status = $row['mystatus']; $score = $row['myscore']; - $time_for_total = $row['mytime']; + $time_for_total += $row['mytime']; $attemptTime = $row['mytime']; if ($minimumAvailable) { @@ -901,12 +901,9 @@ class Tracking $title = Security::remove_XSS($title); $output .= ' '.$extend_link.' - +

'.$title.'

- '.learnpathitem::humanize_status($lesson_status).' - - '.$action.' '; } else { @@ -2562,7 +2559,7 @@ class Tracking * [sum_of_progresses, number] if it is set to true * @param bool $onlySeriousGame Optional. Limit average to lp on seriousgame mode * - * @return float Average progress of the user in this course + * @return float Average progress of the user in this course from 0 to 100 */ public static function get_avg_student_progress( $studentId, @@ -7648,7 +7645,8 @@ class TrackingCourseLog user.official_code as col0, user.lastname as col1, user.firstname as col2, - user.username as col3'; + user.username as col3, + user.email as col4'; if ($getCount) { $select = ' SELECT COUNT(distinct(user.id)) as count '; } @@ -7902,6 +7900,10 @@ class TrackingCourseLog } } + if (api_get_setting('show_email_addresses') === 'true') { + $user_row['email'] = $user['col4']; + } + $user_row['link'] = $user['link']; if ($export_csv) { diff --git a/public/main/inc/lib/usergroup.lib.php b/public/main/inc/lib/usergroup.lib.php index 51a0b63064..c7e4b1c124 100644 --- a/public/main/inc/lib/usergroup.lib.php +++ b/public/main/inc/lib/usergroup.lib.php @@ -195,11 +195,11 @@ class UserGroup extends Model } /** - * @param int $type + * @param string $extraWhereCondition * * @return int */ - public function get_count() + public function get_count($extraWhereCondition = '') { $authorCondition = ''; @@ -217,6 +217,7 @@ class UserGroup extends Model INNER JOIN $this->access_url_rel_usergroup a ON (u.id = a.usergroup_id) WHERE access_url_id = $urlId $authorCondition + $extraWhereCondition "; $result = Database::query($sql); @@ -230,6 +231,7 @@ class UserGroup extends Model FROM {$this->table} a WHERE 1 = 1 $authorCondition + AND $extraWhereCondition "; $result = Database::query($sql); if (Database::num_rows($result)) { @@ -1116,41 +1118,39 @@ class UserGroup extends Model * @param int $sord * @param int $start * @param int $limit + * @param string $extraWhereCondition * * @return array */ - public function getUsergroupsPagination($sidx, $sord, $start, $limit) + public function getUsergroupsPagination($sidx, $sord, $start, $limit, $extraWhereCondition = '') { $sord = in_array(strtolower($sord), ['asc', 'desc']) ? $sord : 'desc'; $start = (int) $start; $limit = (int) $limit; + + $sqlFrom = "{$this->table} u "; + $sqlWhere = '1 = 1 '; + if ($this->getUseMultipleUrl()) { $urlId = api_get_current_access_url_id(); - $from = $this->table." u - INNER JOIN {$this->access_url_rel_usergroup} a - ON (u.id = a.usergroup_id)"; - $where = [' access_url_id = ?' => $urlId]; - } else { - $from = $this->table.' u '; - $where = []; + $sqlFrom .= " INNER JOIN {$this->access_url_rel_usergroup} a ON (u.id = a.usergroup_id) "; + $sqlWhere .= " AND a.access_url_id = $urlId "; } if ($this->allowTeachers()) { if (!api_is_platform_admin()) { $userId = api_get_user_id(); - $where = [' author_id = ?' => $userId]; + $sqlWhere .= " AND author_id = $userId "; } } - $result = Database::select( - 'u.*', - $from, - [ - 'where' => $where, - 'order' => "name $sord", - 'LIMIT' => "$start , $limit", - ] + if ($extraWhereCondition) { + $sqlWhere .= " AND $extraWhereCondition "; + } + + $result = Database::store_result( + Database::query("SELECT u.* FROM $sqlFrom WHERE $sqlWhere ORDER BY name $sord LIMIT $start, $limit") ); $new_result = []; @@ -1439,9 +1439,8 @@ class UserGroup extends Model * @param string $cropParameters * * @return mixed Returns the resulting common file name of created images which usually should be stored in database. - * When an image is removed the function returns an empty string. In case of internal error or negative validation it returns FALSE. - * - * @see UserManager::delete_user_picture() as the prefered way for deletion. + * When an image is removed the function returns an empty string. + * In case of internal error or negative validation it returns FALSE. */ public function update_group_picture($group_id, $file = null, $source_file = null, $cropParameters = null) { @@ -2387,7 +2386,11 @@ class UserGroup extends Model } } - $sql = "SELECT picture_uri as image, u.id, CONCAT (u.firstname,' ', u.lastname) as fullname, relation_type + $sql = "SELECT + picture_uri as image, + u.id, + CONCAT (u.firstname,' ', u.lastname) as fullname, + relation_type FROM $tbl_user u INNER JOIN $table_group_rel_user gu ON (gu.user_id = u.id) @@ -2406,6 +2409,8 @@ class UserGroup extends Model $row['image'] = ''; $row['user_info'] = $userInfo; } + + $row['user_id'] = $row['id']; $array[$row['id']] = $row; } diff --git a/public/main/inc/lib/userportal.lib.php b/public/main/inc/lib/userportal.lib.php index 1853040ae9..15c9895394 100644 --- a/public/main/inc/lib/userportal.lib.php +++ b/public/main/inc/lib/userportal.lib.php @@ -1,4 +1,5 @@ Display::return_icon('lp_student_publication.png', get_lang('StudentPublications')), + 'link' => api_get_path(WEB_CODE_PATH).'work/publications.php', + 'title' => get_lang('MyStudentPublications'), + ]; + } + + return $items; + } + /** * Reacts on a failed login: * Displays an explanation with a link to the registration form.