swad-core/swad_log.c

307 lines
10 KiB
C

// swad_log.c: access log stored in database
/*
SWAD (Shared Workspace At a Distance),
is a web platform developed at the University of Granada (Spain),
and used to support university teaching.
This file is part of SWAD core.
Copyright (C) 1999-2024 Antonio Cañas Vargas
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General 3 License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/*********************************** Headers *********************************/
/*****************************************************************************/
#include <stdlib.h> // For free
#include <string.h> // For strlen
#include "swad_action.h"
#include "swad_action_list.h"
#include "swad_banner.h"
#include "swad_box.h"
#include "swad_center_database.h"
#include "swad_config.h"
#include "swad_database.h"
#include "swad_degree_database.h"
#include "swad_exam_log.h"
#include "swad_global.h"
#include "swad_hierarchy.h"
#include "swad_HTML.h"
#include "swad_institution_database.h"
#include "swad_log.h"
#include "swad_log_database.h"
#include "swad_profile.h"
#include "swad_profile_database.h"
#include "swad_role.h"
#include "swad_statistic.h"
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/************************* Private global variables **************************/
/*****************************************************************************/
static bool Log_Search = false; // Log search in database?
/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/
static bool Log_GetLogSearch (void);
/*****************************************************************************/
/******************** Set/get logging search in database *********************/
/*****************************************************************************/
void Log_SetLogSearch (bool Search)
{
Log_Search = Search;
}
static bool Log_GetLogSearch (void)
{
return Log_Search;
}
/*****************************************************************************/
/**************************** Log access in database *************************/
/*****************************************************************************/
void Log_LogAccess (const char *Comments)
{
long LogCod;
long ActCod = Act_GetActCod (Gbl.Action.Act);
size_t MaxLength;
char *CommentsDB;
long BanCodClicked;
Rol_Role_t RoleToStore = (Gbl.Action.Act == ActLogOut) ? Gbl.Usrs.Me.Role.LoggedBeforeCloseSession :
Gbl.Usrs.Me.Role.Logged;
/***** Insert access into database *****/
/* Log access in historical log */
LogCod = Log_DB_LogAccessInHistoricalLog (ActCod,RoleToStore);
/* Log access in recent log (log_recent) */
Log_DB_LogAccessInRecentLog (LogCod,ActCod,RoleToStore);
/* Log access while answering exam prints */
ExaLog_LogAccess (LogCod);
/* Log comments */
if (Comments)
{
MaxLength = strlen (Comments) * Str_MAX_BYTES_PER_CHAR;
if ((CommentsDB = malloc (MaxLength + 1)) != NULL)
{
Str_Copy (CommentsDB,Comments,MaxLength);
Str_ChangeFormat (Str_FROM_TEXT,Str_TO_TEXT,
CommentsDB,MaxLength,Str_REMOVE_SPACES); // Avoid SQL injection
Log_DB_LogComments (LogCod,CommentsDB);
free (CommentsDB);
}
}
/* Log search string */
if (Log_GetLogSearch ())
Log_DB_LogSearchString (LogCod,Sch_GetSearch ());
if (Gbl.WebService.IsWebService)
/* Log web service plugin and API function */
Log_DB_LogAPI (LogCod);
else
{
BanCodClicked = Ban_GetBanCodClicked ();
if (BanCodClicked > 0)
/* Log banner clicked */
Log_DB_LogBanner (LogCod,BanCodClicked);
}
/***** Increment my number of clicks *****/
if (Gbl.Usrs.Me.Logged)
Prf_DB_IncrementNumClicksUsr (Gbl.Usrs.Me.UsrDat.UsrCod);
}
/*****************************************************************************/
/*************** Put a link to show last clicks in real time *****************/
/*****************************************************************************/
void Log_PutLinkToLastClicks (void)
{
extern const char *Txt_Last_clicks;
Lay_PutContextualLinkIconText (ActLstClk,NULL,
NULL,NULL,
"mouse-pointer.svg",Ico_BLACK,
Txt_Last_clicks,NULL);
}
/*****************************************************************************/
/****************************** Show last clicks *****************************/
/*****************************************************************************/
void Log_ShowLastClicks (void)
{
extern const char *Hlp_USERS_Connected_last_clicks;
extern const char *Txt_Last_clicks_in_real_time;
/***** Contextual menu *****/
Mnu_ContextMenuBegin ();
Sta_PutLinkToGlobalHits (); // Global hits
Sta_PutLinkToCourseHits (); // Course hits
Mnu_ContextMenuEnd ();
/***** Begin box *****/
Box_BoxBegin (Txt_Last_clicks_in_real_time,NULL,NULL,
Hlp_USERS_Connected_last_clicks,Box_NOT_CLOSABLE);
/***** Get and show last clicks *****/
HTM_DIV_Begin ("id=\"lastclicks\" class=\"CM\""); // Used for AJAX based refresh
Log_GetAndShowLastClicks ();
HTM_DIV_End (); // Used for AJAX based refresh
/***** End box *****/
Box_BoxEnd ();
}
/*****************************************************************************/
/**************** Get last clicks from database and show them ****************/
/*****************************************************************************/
void Log_GetAndShowLastClicks (void)
{
extern const char *Txt_Click;
extern const char *Txt_ELAPSED_TIME;
extern const char *Txt_Role;
extern const char *Txt_HIERARCHY_SINGUL_Abc[Hie_NUM_LEVELS];
extern const char *Txt_Action;
extern const char *Txt_ROLES_SINGUL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumClicks;
unsigned NumClick;
long ActCod;
Act_Action_t Action;
const char *ClassRow;
time_t TimeDiff;
struct Hie_Node Hie[Hie_NUM_LEVELS];
Hie_Level_t Level;
/***** Get last clicks from database *****/
NumClicks = Log_DB_GetLastClicks (&mysql_res);
/***** Write list of connected users *****/
HTM_TABLE_Begin ("TBL_SCROLL");
/* Heading row */
HTM_TR_Begin (NULL);
HTM_TH_Span (Txt_Click ,HTM_HEAD_RIGHT,1,1,"LC_CLK"); // Click
HTM_TH_Span (Txt_ELAPSED_TIME ,HTM_HEAD_RIGHT,1,1,"LC_TIM"); // Elapsed time
HTM_TH_Span (Txt_Role ,HTM_HEAD_LEFT ,1,1,"LC_ROL"); // Role
for (Level = Hie_CTY;
Level <= Hie_DEG;
Level++)
HTM_TH_Span (Txt_HIERARCHY_SINGUL_Abc[Level],HTM_HEAD_LEFT ,1,1,"LC_HIE"); // Country, Institution, Center, Degree
HTM_TH_Span (Txt_Action ,HTM_HEAD_LEFT ,1,1,"LC_ACT"); // Action
HTM_TR_End ();
for (NumClick = 0;
NumClick < NumClicks;
NumClick++)
{
row = mysql_fetch_row (mysql_res);
/* Get action code (row[1]) */
ActCod = Str_ConvertStrCodToLongCod (row[1]);
Action = Act_GetActionFromActCod (ActCod);
/* Use a special color for this row depending on the action */
ClassRow = (Act_GetBrowserTab (Action) == Act_DWN) ? "DAT_SMALL_YELLOW" :
(ActCod == Act_GetActCod (ActLogIn ) ||
ActCod == Act_GetActCod (ActLogInNew)) ? "DAT_SMALL_GREEN" :
(ActCod == Act_GetActCod (ActLogOut )) ? "DAT_SMALL_RED" :
(ActCod == Act_GetActCod (ActWebSvc )) ? "DAT_SMALL_BLUE" :
"DAT_SMALL";
/* Compute elapsed time from last access */
if (sscanf (row[2],"%ld",&TimeDiff) != 1)
TimeDiff = (time_t) 0;
/* Get country code (row[4]) */
Hie[Hie_CTY].HieCod = Str_ConvertStrCodToLongCod (row[4]);
Cty_GetCountryNameInLanguage (Hie[Hie_CTY].HieCod,Gbl.Prefs.Language,
Hie[Hie_CTY].FullName);
/* Get institution code (row[5]),
center code (row[6])
and degree code (row[7]) */
Hie[Hie_INS].HieCod = Str_ConvertStrCodToLongCod (row[5]);
Hie[Hie_CTR].HieCod = Str_ConvertStrCodToLongCod (row[6]);
Hie[Hie_DEG].HieCod = Str_ConvertStrCodToLongCod (row[7]);
Ins_DB_GetInsShrtName (Hie[Hie_INS].HieCod,Hie[Hie_INS].ShrtName);
Ctr_DB_GetCtrShrtName (Hie[Hie_CTR].HieCod,Hie[Hie_CTR].ShrtName);
Deg_DB_GetDegShrtName (Hie[Hie_DEG].HieCod,Hie[Hie_DEG].ShrtName);
/* Print table row */
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"LC_CLK %s_%s\"",
ClassRow,The_GetSuffix ());
HTM_Txt (row[0]); // Click
HTM_TD_End ();
HTM_TD_Begin ("class=\"LC_TIM %s_%s\"",
ClassRow,The_GetSuffix ());
Dat_WriteHoursMinutesSecondsFromSeconds (TimeDiff); // Elapsed time
HTM_TD_End ();
HTM_TD_Begin ("class=\"LC_ROL %s_%s\"",
ClassRow,The_GetSuffix ());
HTM_Txt (Txt_ROLES_SINGUL_Abc[Rol_ConvertUnsignedStrToRole (row[3])][Usr_SEX_UNKNOWN]); // Role
HTM_TD_End ();
HTM_TD_Begin ("class=\"LC_HIE %s_%s\"",
ClassRow,The_GetSuffix ());
HTM_Txt (Hie[Hie_CTY].FullName); // Country
HTM_TD_End ();
for (Level = Hie_INS;
Level <= Hie_DEG;
Level++)
{
HTM_TD_Begin ("class=\"LC_HIE %s_%s\"",
ClassRow,The_GetSuffix ());
HTM_Txt (Hie[Level].ShrtName); // Institution, Center, Degree
HTM_TD_End ();
}
HTM_TD_Begin ("class=\"LC_ACT %s_%s\"",
ClassRow,The_GetSuffix ());
HTM_Txt (Act_GetActionText (Action)); // Action
HTM_TD_End ();
HTM_TR_End ();
}
HTM_TABLE_End ();
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}