Internal: Update from 1.11.x

pull/3466/head
Julio Montoya 5 years ago
parent 966b125849
commit 99d6f83754
  1. 51
      public/main/inc/lib/exercise.lib.php
  2. 30
      public/main/inc/lib/fileUpload.lib.php
  3. 36
      public/main/inc/lib/lp_item.lib.php
  4. 286
      public/main/inc/lib/message.lib.php
  5. 44
      public/main/inc/lib/online.inc.php
  6. 11
      public/main/inc/lib/security.lib.php
  7. 4
      public/main/inc/lib/social.lib.php
  8. 3
      public/main/inc/lib/sortable_table.class.php
  9. 302
      public/main/inc/lib/statistics.lib.php
  10. 7
      public/main/inc/lib/thematic.lib.php
  11. 16
      public/main/inc/lib/tracking.lib.php
  12. 51
      public/main/inc/lib/usergroup.lib.php
  13. 21
      public/main/inc/lib/userportal.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 .= '
<div class="col-md-12 ui-widget ui-helper-clearfix">
<div class="clearfix">
<ul class="exercise-draggable-answer '.($isVertical ? '' : 'list-inline').'"
id="question-'.$questionId.'" data-question="'.$questionId.'">
<div class="row">
<div class="col-md-12">
<p class="small">'.get_lang('DraggableQuestionIntro').'</p>
<ul class="exercise-draggable-answer list-unstyled '
.($isVertical ? '' : 'list-inline').'" id="question-'.$questionId.'" data-question="'
.$questionId.'">
';
} else {
$s .= '<div id="drag'.$questionId.'_question" class="drag_question">
@ -1352,10 +1363,13 @@ HTML;
$s .= '</table>';
}
if (DRAGGABLE == $answerType) {
$isVertical = 'v' == $objQuestionTmp->extra;
$s .= "</ul>";
$s .= "</div></div>"; // col-md-12
if ($answerType == DRAGGABLE) {
$isVertical = $objQuestionTmp->extra == 'v';
$s .= "
</ul>
</div><!-- .col-md-12 -->
</div><!-- .row -->
";
$counterAnswer = 1;
$s .= $isVertical ? '' : '<div class="row">';
for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
@ -1378,7 +1392,7 @@ HTML;
}
$s .= $isVertical ? '' : '</div>'; // row
$s .= '</div>'; // col-md-12 ui-widget ui-helper-clearfix
// $s .= '</div>';
}
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 <<<HOTSPOT
<input type="hidden" name="hidden_hotspot_id" value="$questionId" />
@ -1532,6 +1553,13 @@ HOTSPOT;
}
echo $objQuestionTmp->getTitleToDisplay($current_item);
}
if ($questionRequireAuth) {
WhispeakAuthPlugin::quizQuestionAuthentify($questionId, $exercise);
return false;
}
echo '
<input type="hidden" name="hidden_hotspot_id" value="'.$questionId.'" />
<div class="exercise_questions">
@ -4512,7 +4540,6 @@ EOT;
);
} else {
$pluginEvaluation = QuestionOptionsEvaluationPlugin::create();
if ('true' === $pluginEvaluation->get(QuestionOptionsEvaluationPlugin::SETTING_ENABLE)) {
$formula = $pluginEvaluation->getFormulaForExercise($objExercise->selectId());

@ -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)

@ -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);
}
}

@ -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"), '<br />', $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
).' <br />'.$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')).'&nbsp;<b>-</b></li>';
$message_content .= '&nbsp;'.api_strtolower(get_lang('To')).'&nbsp;'.get_lang('Me');
break;
case self::MESSAGE_TYPE_OUTBOX:
@ -1475,12 +1510,18 @@ class MessageManager
$message_content .= '<a href="new_message.php?re_id='.$messageId.'&'.$social_link.'">'.
Display::return_icon('message_reply.png', get_lang('Reply to this message')).'</a> &nbsp';
$message_content .= '<a href="inbox.php?action=deleteone&id='.$messageId.'&'.$social_link.'" >'.
Display::return_icon('delete.png', get_lang('Delete message')).'</a>&nbsp';
Display::return_icon('delete.png', get_lang('DeleteMessage')).'</a>&nbsp;';
if ($idPrevMessage != 0) {
$message_content .= '<a title="'.get_lang('PrevMessage').'" href="view_message.php?type='.$type.'&id='.$idPrevMessage.'" ">'.Display::return_icon('icons/22/back.png', get_lang('ScormPrevious')).'</a> &nbsp';
}
if ($idNextMessage != 0) {
$message_content .= '<a title="'.get_lang('NextMessage').'" href="view_message.php?type='.$type.'&id='.$idNextMessage.'">'.Display::return_icon('icons/22/move.png', get_lang('ScormNext')).'</a> &nbsp';
}
break;
}
$message_content .= '</div></td>
<td width=10></td>
<td width="10"></td>
</tr>
</table>';
@ -2162,7 +2203,6 @@ class MessageManager
20,
'DESC'
);
$table->setDataFunctionParams(
['keyword' => $keyword, 'type' => $type, 'actions' => $actions]
);

@ -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);
}
}

@ -1,4 +1,5 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Component\HTMLPurifier\Filter\AllowIframes;
@ -141,7 +142,7 @@ class Security
*
* @return bool True if it's the right token, false otherwise
*/
public static function check_token($request_type = 'post')
public static function check_token($request_type = 'post', FormValidator $form = null)
{
$sessionToken = Session::read('sec_token');
switch ($request_type) {
@ -162,6 +163,14 @@ class Security
return true;
}
return false;
case 'form':
$token = $form->getSubmitValue('protect_token');
if (!empty($sessionToken) && !empty($token) && $sessionToken === $token) {
return true;
}
return false;
default:
if (!empty($sessionToken) && isset($request_type) && $sessionToken === $request_type) {

@ -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'));
}
/**

@ -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';

@ -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 '<table class="data_table" cellspacing="0" cellpadding="3">
$content = '<table class="data_table" cellspacing="0" cellpadding="3" width="90%">
<tr><th colspan="'.($showTotal ? '4' : '3').'">'.$title.'</th></tr>';
$i = 0;
foreach ($stats as $subtitle => $number) {
@ -482,14 +488,14 @@ class Statistics
}
$percentage = ($total > 0 ? number_format(100 * $number / $total, 1, ',', '.') : '0');
echo '<tr class="row_'.(0 == $i % 2 ? 'odd' : 'even').'">
<td width="150">'.$subtitle.'</td>
<td width="550">'.Display::bar_progress($percentage, false).'</td>
<td align="right">'.$number_label.'</td>';
$content .= '<tr class="row_'.($i % 2 == 0 ? 'odd' : 'even').'">
<td width="25%" style="vertical-align:top;">'.$subtitle.'</td>
<td width="60%">'.Display::bar_progress($percentage, false).'</td>
<td width="5%" align="right" style="vertical-align:top;">'.$number_label.'</td>';
if ($showTotal) {
echo '<td align="right"> '.$percentage.'%</td>';
$content .= '<td width="5%" align="right"> '.$percentage.'%</td>';
}
echo '</tr>';
$content .= '</tr>';
$i++;
}
if ($showTotal) {
@ -498,9 +504,11 @@ class Statistics
} else {
$total_label = self::makeSizeString($total);
}
echo '<tr><th colspan="4" align="right">'.get_lang('Total').': '.$total_label.'</td></tr>';
$content .= '<tr><th colspan="4" align="right">'.get_lang('Total').': '.$total_label.'</td></tr>';
}
echo '</table>';
$content .= '</table>';
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 '<br />';
$content .= '<br />';
}
$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 .= " <br /> $localDate - $localEndDate";
$label .= " <span class=\"muted right\" style=\"float: right; margin-right: 5px;\">[$localDate - $localEndDate]</span>";
$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 '<h4>'.get_lang('Important activities').'</h4>';
$content = '<h4>'.get_lang('Important activities').'</h4>';
// 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 '<div class="actions">';
$form->display();
echo '</div>';
$content .= '<div class="actions">';
$content .= $form->returnForm();
$content .= '</div>';
$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 '<p>'.get_lang('Latest access').' &gt;= '.$date_diff.' '.get_lang('days').'</p>';
$content .= '<p>'.get_lang('Latest access').' &gt;= '.$date_diff.' '.get_lang('days').'</p>';
$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 = '
<script>
$(function() {
Chart.defaults.global.responsive = true;
var ctx = document.getElementById("'.$elementId.'").getContext("2d");
var chart = new Chart(ctx, {
type: "'.$type.'",
data: '.$data.',
options: {'.$options.'}
});
});
</script>';
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 = '<div style="width: 200px; overflow-wrap: break-word;">'.$content.'</div>';
$table->setCellContents(
$row,
$column++,
$content
);
$row++;
}
return $table->toHtml();
}
echo $content;
return '<table id="table_'.$bossId.'"></table>';
}
/**

@ -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) {

@ -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 .= '<tr class="'.$oddclass.'">
<td>'.$extend_link.'</td>
<td colspan="4">
<td colspan="10">
<h4>'.$title.'</h4>
</td>
<td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
<td colspan="2"></td>
<td colspan="2"></td>
'.$action.'
</tr>';
} 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) {

@ -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'] = '<img src="'.$userPicture.'" />';
$row['user_info'] = $userInfo;
}
$row['user_id'] = $row['id'];
$array[$row['id']] = $row;
}

@ -1,4 +1,5 @@
<?php
/* For licensing terms, see /license.txt */
/**
@ -304,6 +305,26 @@ class IndexManager
return $items;
}
public static function studentPublicationBlock()
{
if (api_is_anonymous()) {
return [];
}
$allow = api_get_configuration_value('allow_my_student_publication_page');
$items = [];
if ($allow) {
$items[] = [
'icon' => 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.

Loading…
Cancel
Save