Version 15.234

This commit is contained in:
Antonio Cañas Vargas 2016-06-29 18:27:49 +02:00
parent 9ebfc85f69
commit f104cf00de
9 changed files with 330 additions and 196 deletions

View File

@ -410,9 +410,11 @@ int swad__getTests (char *wsKey,int courseCode,long beginTime,
int swad__getTrivialQuestion (char *wsKey,char *degrees,float lowerScore,float upperScore, int swad__getTrivialQuestion (char *wsKey,char *degrees,float lowerScore,float upperScore,
struct swad__getTrivialQuestionOutput *getTrivialQuestionOut); struct swad__getTrivialQuestionOutput *getTrivialQuestionOut);
/* List of users of a course / group */ /* List of users */
int swad__getUsers (char *wsKey,int courseCode,int groupCode,int userRole, int swad__getUsers (char *wsKey,int courseCode,char *groups,int userRole,
struct swad__getUsersOutput *getUsersOut); struct swad__getUsersOutput *getUsersOut);
int swad__findUsers (char *wsKey,int courseCode,char *filter,int userRole,
struct swad__getUsersOutput *getUsersOut);
/* Control of attendance */ /* Control of attendance */
int swad__getAttendanceEvents (char *wsKey,int courseCode, int swad__getAttendanceEvents (char *wsKey,int courseCode,

View File

@ -70,7 +70,6 @@ typedef enum
static bool ID_CheckIfUsrIDIsValidUsingMinDigits (const char *UsrID,unsigned MinDigits); static bool ID_CheckIfUsrIDIsValidUsingMinDigits (const char *UsrID,unsigned MinDigits);
static bool ID_ICanSeeAnotherUsrID (struct UsrData *UsrDat);
static void ID_PutButtonToReqConfirmID (struct UsrData *UsrDat,unsigned NumID); static void ID_PutButtonToReqConfirmID (struct UsrData *UsrDat,unsigned NumID);
static void ID_PutButtonToConfirmID (struct UsrData *UsrDat,unsigned NumID); static void ID_PutButtonToConfirmID (struct UsrData *UsrDat,unsigned NumID);
@ -405,7 +404,7 @@ void ID_WriteUsrIDs (struct UsrData *UsrDat)
/*****************************************************************************/ /*****************************************************************************/
// This function should not be called when UsrDat->UsrCod == Gbl.Usrs.Me.UsrDat.UsrCod // This function should not be called when UsrDat->UsrCod == Gbl.Usrs.Me.UsrDat.UsrCod
static bool ID_ICanSeeAnotherUsrID (struct UsrData *UsrDat) bool ID_ICanSeeAnotherUsrID (struct UsrData *UsrDat)
{ {
/***** Check if I have permission to see another user's IDs *****/ /***** Check if I have permission to see another user's IDs *****/
switch (Gbl.Usrs.Me.LoggedRole) switch (Gbl.Usrs.Me.LoggedRole)

View File

@ -65,6 +65,7 @@ bool ID_CheckIfUsrIDIsValid (const char *UsrID);
bool ID_CheckIfUsrIDSeemsAValidID (const char *UsrID); bool ID_CheckIfUsrIDSeemsAValidID (const char *UsrID);
void ID_WriteUsrIDs (struct UsrData *UsrDat); void ID_WriteUsrIDs (struct UsrData *UsrDat);
bool ID_ICanSeeAnotherUsrID (struct UsrData *UsrDat);
void ID_PutLinkToChangeUsrIDs (void); void ID_PutLinkToChangeUsrIDs (void);
void ID_ShowFormOthIDs (void); void ID_ShowFormOthIDs (void);

View File

@ -121,6 +121,11 @@
// TODO: Messages in msg_content_deleted older than a certain time should be deleted to ensure the protection of personal data // TODO: Messages in msg_content_deleted older than a certain time should be deleted to ensure the protection of personal data
// TODO: FIX BUG: A teacher uploads a document in course documents zone, then he/she unregister from course, then he/she search for his/her documents, a document is shown in results but he/she can not view it // TODO: FIX BUG: A teacher uploads a document in course documents zone, then he/she unregister from course, then he/she search for his/her documents, a document is shown in results but he/she can not view it
// TODO: Add Stack Exchange to webs/networks // TODO: Add Stack Exchange to webs/networks
// TODO: Optimize slow query searching messages received
// TODO: FIX BUG: Searching messages received gives unordered list
// TODO: Add file size to summary in notifications of new files.
// TODO: Put Raúl Hinojosa (iSWAD developer) in a row of marks file of EC (B,C) and publish file
// TODO: Modify WS function getUsers changing: userRole to indicate all users, and a new parameter filter (search string (name, @nickname, mail)) to restring number of users // TODO: Modify WS function getUsers changing: userRole to indicate all users, and a new parameter filter (search string (name, @nickname, mail)) to restring number of users
// TODO: Add a new WS function to count the nunmber of users to return in call to function getUsers // TODO: Add a new WS function to count the nunmber of users to return in call to function getUsers
@ -129,13 +134,14 @@
/****************************** Public constants *****************************/ /****************************** Public constants *****************************/
/*****************************************************************************/ /*****************************************************************************/
#define Log_PLATFORM_VERSION "SWAD 15.233.4 (2016-06-27)" #define Log_PLATFORM_VERSION "SWAD 15.234 (2016-06-29)"
#define CSS_FILE "swad15.229.css" #define CSS_FILE "swad15.229.css"
#define JS_FILE "swad15.226.js" #define JS_FILE "swad15.226.js"
// Number of lines (includes comments but not blank lines) has been got with the following command: // Number of lines (includes comments but not blank lines) has been got with the following command:
// nl swad*.c swad*.h css/swad*.css py/swad*.py js/swad*.js soap/swad*.h sql/swad*.sql | tail -1 // nl swad*.c swad*.h css/swad*.css py/swad*.py js/swad*.js soap/swad*.h sql/swad*.sql | tail -1
/* /*
Version 15.234: Jun 29, 2015 New web service functions getUsers and findUsers. (203169 lines)
Version 15.233.4: Jun 27, 2015 Changes in links in list of notifications. (203065 lines) Version 15.233.4: Jun 27, 2015 Changes in links in list of notifications. (203065 lines)
Version 15.233.3: Jun 27, 2015 Fixed bug in list of admins. (203074 lines) Version 15.233.3: Jun 27, 2015 Fixed bug in list of admins. (203074 lines)
Version 15.233.2: Jun 27, 2015 Code refactoring in search of users. (203072 lines) Version 15.233.2: Jun 27, 2015 Code refactoring in search of users. (203072 lines)

View File

@ -41,11 +41,8 @@
/***************************** Internal constants ****************************/ /***************************** Internal constants ****************************/
/*****************************************************************************/ /*****************************************************************************/
#define Sch_MAX_WORDS_IN_SEARCH 10
#define Sch_MAX_LENGTH_SEARCH_WORD 255
#define Sch_MIN_LENGTH_LONGEST_WORD 3 #define Sch_MIN_LENGTH_LONGEST_WORD 3
#define Sch_MIN_LENGTH_TOTAL 7 // "A An Ann" is not valid; "A An Ann Anna" is valid #define Sch_MIN_LENGTH_TOTAL 7 // "A An Ann" is not valid; "A An Ann Anna" is valid
#define Sch_MAX_LENGTH_SEARCH_QUERY (10*Sch_MAX_LENGTH_STRING_TO_FIND)
/*****************************************************************************/ /*****************************************************************************/
/****************************** Internal types *******************************/ /****************************** Internal types *******************************/
@ -77,8 +74,6 @@ static unsigned Sch_SearchUsrsInDB (Rol_Role_t Role);
static unsigned Sch_SearchOpenDocumentsInDB (const char *RangeQuery); static unsigned Sch_SearchOpenDocumentsInDB (const char *RangeQuery);
static unsigned Sch_SearchDocumentsInMyCoursesInDB (const char *RangeQuery); static unsigned Sch_SearchDocumentsInMyCoursesInDB (const char *RangeQuery);
static unsigned Sch_SearchMyDocumentsInDB (const char *RangeQuery); static unsigned Sch_SearchMyDocumentsInDB (const char *RangeQuery);
static bool Sch_BuildSearchQuery (char *SearchQuery,const char *FieldName,
const char *CharSet,const char *Collate);
static void Sch_SaveLastSearchIntoSession (void); static void Sch_SaveLastSearchIntoSession (void);
@ -793,25 +788,29 @@ static unsigned Sch_SearchCoursesInDB (const char *RangeQuery)
} }
/*****************************************************************************/ /*****************************************************************************/
/************************* Search teachers in database ***********************/ /*************************** Search users in database ************************/
/*****************************************************************************/ /*****************************************************************************/
// Returns number of teachers found // Returns number of users found
static unsigned Sch_SearchUsrsInDB (Rol_Role_t Role) static unsigned Sch_SearchUsrsInDB (Rol_Role_t Role)
{ {
extern const char *Txt_The_search_text_must_be_longer;
static bool WarningMessageWritten = false;
char SearchQuery[Sch_MAX_LENGTH_SEARCH_QUERY+1]; char SearchQuery[Sch_MAX_LENGTH_SEARCH_QUERY+1];
/***** Check user's permission *****/ /***** Split user string into words *****/
if (Sch_CheckIfIHavePermissionToSearch ( Role == Rol_UNKNOWN ? Sch_SEARCH_USERS : if (Sch_BuildSearchQuery (SearchQuery,
(Role == Rol_TEACHER ? Sch_SEARCH_TEACHERS : "CONCAT_WS(' ',FirstName,Surname1,Surname2)",
(Role == Rol_STUDENT ? Sch_SEARCH_STUDENTS : NULL,NULL))
Sch_SEARCH_GUESTS)))) /***** Query database and list users found *****/
/***** Split user string into words *****/ return Usr_ListUsrsFound (Role,SearchQuery);
if (Sch_BuildSearchQuery (SearchQuery, else
"CONCAT_WS(' ',usr_data.FirstName,usr_data.Surname1,usr_data.Surname2)", // Too short
NULL,NULL)) if (!WarningMessageWritten) // To avoid repetitions
/***** Query database and list users found *****/ {
return Usr_ListUsrsFound (Role,SearchQuery); Lay_ShowAlert (Lay_WARNING,Txt_The_search_text_must_be_longer);
WarningMessageWritten = true;
}
return 0; return 0;
} }
@ -1215,11 +1214,9 @@ static unsigned Sch_SearchMyDocumentsInDB (const char *RangeQuery)
// Returns true if a valid search query is built // Returns true if a valid search query is built
// Returns false when no valid search query // Returns false when no valid search query
static bool Sch_BuildSearchQuery (char *SearchQuery,const char *FieldName, bool Sch_BuildSearchQuery (char *SearchQuery,const char *FieldName,
const char *CharSet,const char *Collate) const char *CharSet,const char *Collate)
{ {
extern const char *Txt_The_search_text_must_be_longer;
static bool WarningMessageWritten = false;
const char *Ptr; const char *Ptr;
unsigned NumWords; unsigned NumWords;
unsigned NumWord; unsigned NumWord;
@ -1229,74 +1226,71 @@ static bool Sch_BuildSearchQuery (char *SearchQuery,const char *FieldName,
char SearchWords[Sch_MAX_WORDS_IN_SEARCH][Sch_MAX_LENGTH_SEARCH_WORD+1]; char SearchWords[Sch_MAX_WORDS_IN_SEARCH][Sch_MAX_LENGTH_SEARCH_WORD+1];
bool SearchWordIsValid = true; bool SearchWordIsValid = true;
SearchQuery[0] = '\0'; if (Gbl.Search.Str[0])
Ptr = Gbl.Search.Str;
for (NumWords = 0;
NumWords < Sch_MAX_WORDS_IN_SEARCH && *Ptr;
NumWords++)
{ {
/* Get next word */ SearchQuery[0] = '\0';
Str_GetNextStringUntilSpace (&Ptr,SearchWords[NumWords],Sch_MAX_LENGTH_SEARCH_WORD); Ptr = Gbl.Search.Str;
for (NumWords = 0;
/* Is this word valid? */ NumWords < Sch_MAX_WORDS_IN_SEARCH && *Ptr;
switch (Gbl.Search.WhatToSearch) NumWords++)
{
case Sch_SEARCH_OPEN_DOCUMENTS:
case Sch_SEARCH_DOCUM_IN_MY_COURSES:
case Sch_SEARCH_MY_DOCUMENTS:
SearchWordIsValid = Str_ConvertFilFolLnkNameToValid (SearchWords[NumWords]);
break;
default:
SearchWordIsValid = true;
break;
}
/* Check if this word is repeated (case insensitive) */
for (NumWord = 0;
SearchWordIsValid && NumWord < NumWords;
NumWord++)
if (!strcasecmp (SearchWords[NumWord],SearchWords[NumWords]))
SearchWordIsValid = false;
/* Concatenate word to search string */
if (SearchWordIsValid)
{ {
LengthWord = strlen (SearchWords[NumWords]); /* Get next word */
LengthTotal += LengthWord; Str_GetNextStringUntilSpace (&Ptr,SearchWords[NumWords],Sch_MAX_LENGTH_SEARCH_WORD);
if (LengthWord > MaxLengthWord)
MaxLengthWord = LengthWord;
if (strlen (SearchQuery) + LengthWord + 512 > Sch_MAX_LENGTH_SEARCH_QUERY) // Prevent string overflow
break;
if (NumWords)
strcat (SearchQuery," AND ");
strcat (SearchQuery,FieldName);
strcat (SearchQuery," LIKE ");
if (CharSet)
if (CharSet[0])
strcat (SearchQuery,CharSet);
strcat (SearchQuery,"'%");
strcat (SearchQuery,SearchWords[NumWords]);
strcat (SearchQuery,"%'");
if (Collate)
if (Collate[0])
strcat (SearchQuery,Collate);
}
}
/***** If search string valid? *****/ /* Is this word valid? */
if (LengthTotal < Sch_MIN_LENGTH_TOTAL || switch (Gbl.Search.WhatToSearch)
MaxLengthWord < Sch_MIN_LENGTH_LONGEST_WORD) {
{ case Sch_SEARCH_OPEN_DOCUMENTS:
// Too short case Sch_SEARCH_DOCUM_IN_MY_COURSES:
if (!WarningMessageWritten) // To avoid repetitions case Sch_SEARCH_MY_DOCUMENTS:
{ SearchWordIsValid = Str_ConvertFilFolLnkNameToValid (SearchWords[NumWords]);
Lay_ShowAlert (Lay_WARNING,Txt_The_search_text_must_be_longer); break;
WarningMessageWritten = true; default:
SearchWordIsValid = true;
break;
}
/* Check if this word is repeated (case insensitive) */
for (NumWord = 0;
SearchWordIsValid && NumWord < NumWords;
NumWord++)
if (!strcasecmp (SearchWords[NumWord],SearchWords[NumWords]))
SearchWordIsValid = false;
/* Concatenate word to search string */
if (SearchWordIsValid)
{
LengthWord = strlen (SearchWords[NumWords]);
LengthTotal += LengthWord;
if (LengthWord > MaxLengthWord)
MaxLengthWord = LengthWord;
if (strlen (SearchQuery) + LengthWord + 512 > Sch_MAX_LENGTH_SEARCH_QUERY) // Prevent string overflow
break;
if (NumWords)
strcat (SearchQuery," AND ");
strcat (SearchQuery,FieldName);
strcat (SearchQuery," LIKE ");
if (CharSet)
if (CharSet[0])
strcat (SearchQuery,CharSet);
strcat (SearchQuery,"'%");
strcat (SearchQuery,SearchWords[NumWords]);
strcat (SearchQuery,"%'");
if (Collate)
if (Collate[0])
strcat (SearchQuery,Collate);
}
} }
return false;
} /***** If search string valid? *****/
else if (LengthTotal < Sch_MIN_LENGTH_TOTAL ||
MaxLengthWord < Sch_MIN_LENGTH_LONGEST_WORD)
return false;
return true; return true;
}
return false;
} }
/*****************************************************************************/ /*****************************************************************************/

View File

@ -31,7 +31,10 @@
/****************************** Public constants *****************************/ /****************************** Public constants *****************************/
/*****************************************************************************/ /*****************************************************************************/
#define Sch_MAX_LENGTH_STRING_TO_FIND 255 #define Sch_MAX_LENGTH_STRING_TO_FIND 255
#define Sch_MAX_WORDS_IN_SEARCH 10
#define Sch_MAX_LENGTH_SEARCH_WORD 255
#define Sch_MAX_LENGTH_SEARCH_QUERY (Sch_MAX_WORDS_IN_SEARCH*Sch_MAX_LENGTH_SEARCH_WORD)
/*****************************************************************************/ /*****************************************************************************/
/******************************** Public types *******************************/ /******************************** Public types *******************************/
@ -78,4 +81,7 @@ void Sch_CtrSearch (void);
void Sch_DegSearch (void); void Sch_DegSearch (void);
void Sch_CrsSearch (void); void Sch_CrsSearch (void);
bool Sch_BuildSearchQuery (char *SearchQuery,const char *FieldName,
const char *CharSet,const char *Collate);
#endif #endif

View File

@ -142,9 +142,6 @@ static void Usr_WriteUsrData (const char *BgColor,
bool NonBreak,bool Accepted); bool NonBreak,bool Accepted);
static void Usr_BuildQueryToGetUsrsLstCrs (Rol_Role_t Role,char *Query); static void Usr_BuildQueryToGetUsrsLstCrs (Rol_Role_t Role,char *Query);
static void Usr_SearchListUsrs (Rol_Role_t Role);
static void Usr_CreateTmpTableAndSearchCandidateUsrs (const char *UsrQuery);
static void Usr_DropTmpTableWithCandidateUsrs (void);
static void Usr_GetAdmsLst (Sco_Scope_t Scope); static void Usr_GetAdmsLst (Sco_Scope_t Scope);
static void Usr_GetGstsLst (Sco_Scope_t Scope); static void Usr_GetGstsLst (Sco_Scope_t Scope);
@ -3802,7 +3799,7 @@ void Usr_GetListUsrs (Rol_Role_t Role,Sco_Scope_t Scope)
/********* Search list of users with a given role in current scope ***********/ /********* Search list of users with a given role in current scope ***********/
/*****************************************************************************/ /*****************************************************************************/
static void Usr_SearchListUsrs (Rol_Role_t Role) void Usr_SearchListUsrs (Rol_Role_t Role)
{ {
char Query[4*1024]; char Query[4*1024];
const char *OrderQuery = "candidate_users.UsrCod=usr_data.UsrCod" const char *OrderQuery = "candidate_users.UsrCod=usr_data.UsrCod"
@ -4011,7 +4008,7 @@ static void Usr_SearchListUsrs (Rol_Role_t Role)
/*************** Create temporary table with candidate users *****************/ /*************** Create temporary table with candidate users *****************/
/*****************************************************************************/ /*****************************************************************************/
static void Usr_CreateTmpTableAndSearchCandidateUsrs (const char *UsrQuery) void Usr_CreateTmpTableAndSearchCandidateUsrs (const char *SearchQuery)
{ {
char Query[16*1024]; char Query[16*1024];
@ -4024,7 +4021,7 @@ static void Usr_CreateTmpTableAndSearchCandidateUsrs (const char *UsrQuery)
sprintf (Query,"CREATE TEMPORARY TABLE candidate_users" sprintf (Query,"CREATE TEMPORARY TABLE candidate_users"
" (UsrCod INT NOT NULL,UNIQUE INDEX(UsrCod)) ENGINE=MEMORY" " (UsrCod INT NOT NULL,UNIQUE INDEX(UsrCod)) ENGINE=MEMORY"
" SELECT UsrCod FROM usr_data WHERE %s", " SELECT UsrCod FROM usr_data WHERE %s",
UsrQuery); SearchQuery);
if (mysql_query (&Gbl.mysql,Query)) if (mysql_query (&Gbl.mysql,Query))
DB_ExitOnMySQLError ("can not create temporary table"); DB_ExitOnMySQLError ("can not create temporary table");
} }
@ -4033,7 +4030,7 @@ static void Usr_CreateTmpTableAndSearchCandidateUsrs (const char *UsrQuery)
/***************** Drop temporary table with candidate users *****************/ /***************** Drop temporary table with candidate users *****************/
/*****************************************************************************/ /*****************************************************************************/
static void Usr_DropTmpTableWithCandidateUsrs (void) void Usr_DropTmpTableWithCandidateUsrs (void)
{ {
char Query[128]; char Query[128];
@ -5802,24 +5799,24 @@ void Usr_ListAllDataTchs (void)
/*****************************************************************************/ /*****************************************************************************/
// Returns number of users found // Returns number of users found
unsigned Usr_ListUsrsFound (Rol_Role_t Role,const char *UsrQuery) unsigned Usr_ListUsrsFound (Rol_Role_t Role,const char *SearchQuery)
{ {
extern const char *Txt_user[Usr_NUM_SEXS]; extern const char *Txt_user[Usr_NUM_SEXS];
extern const char *Txt_users[Usr_NUM_SEXS]; extern const char *Txt_users[Usr_NUM_SEXS];
extern const char *Txt_ROLES_PLURAL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS]; extern const char *Txt_ROLES_PLURAL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
extern const char *Txt_ROLES_SINGUL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS]; extern const char *Txt_ROLES_SINGUL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
extern const char *Txt_ROLES_PLURAL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS]; extern const char *Txt_ROLES_PLURAL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
Usr_Sex_t Sex; unsigned NumUsrs;
unsigned NumUsr; unsigned NumUsr;
struct UsrData UsrDat; struct UsrData UsrDat;
unsigned NumUsrs; Usr_Sex_t Sex;
/***** Initialize field names *****/ /***** Initialize field names *****/
Usr_SetUsrDatMainFieldNames (); Usr_SetUsrDatMainFieldNames ();
/***** Create temporary table with candidate users *****/ /***** Create temporary table with candidate users *****/
// Search is faster (aproximately x2) using temporary tables // Search is faster (aproximately x2) using temporary tables
Usr_CreateTmpTableAndSearchCandidateUsrs (UsrQuery); Usr_CreateTmpTableAndSearchCandidateUsrs (SearchQuery);
/***** Search for users *****/ /***** Search for users *****/
Usr_SearchListUsrs (Role); Usr_SearchListUsrs (Role);
@ -5884,7 +5881,7 @@ unsigned Usr_ListUsrsFound (Rol_Role_t Role,const char *UsrQuery)
/***** Free memory for teachers list *****/ /***** Free memory for teachers list *****/
Usr_FreeUsrsList (Role); Usr_FreeUsrsList (Role);
/***** Drop temporary tables *****/ /***** Drop temporary table with candidate users *****/
Usr_DropTmpTableWithCandidateUsrs (); Usr_DropTmpTableWithCandidateUsrs ();
return NumUsrs; return NumUsrs;

View File

@ -305,6 +305,10 @@ unsigned Usr_GetNumberOfTeachersInCentre (long CtrCod);
void Usr_GetListUsrs (Rol_Role_t Role,Sco_Scope_t Scope); void Usr_GetListUsrs (Rol_Role_t Role,Sco_Scope_t Scope);
void Usr_SearchListUsrs (Rol_Role_t Role);
void Usr_CreateTmpTableAndSearchCandidateUsrs (const char *UsrQuery);
void Usr_DropTmpTableWithCandidateUsrs (void);
void Usr_GetUnorderedStdsCodesInDeg (long DegCod); void Usr_GetUnorderedStdsCodesInDeg (long DegCod);
void Usr_FreeUsrsList (Rol_Role_t Role); void Usr_FreeUsrsList (Rol_Role_t Role);
bool Usr_GetIfShowBigList (unsigned NumUsrs); bool Usr_GetIfShowBigList (unsigned NumUsrs);
@ -332,7 +336,7 @@ void Usr_ListAllDataGsts (void);
void Usr_ListAllDataStds (void); void Usr_ListAllDataStds (void);
void Usr_ListUsrsForSelection (Rol_Role_t Role); void Usr_ListUsrsForSelection (Rol_Role_t Role);
void Usr_ListAllDataTchs (void); void Usr_ListAllDataTchs (void);
unsigned Usr_ListUsrsFound (Rol_Role_t Role,const char *UsrQuery); unsigned Usr_ListUsrsFound (Rol_Role_t Role,const char *SearchQuery);
void Usr_ListDataAdms (void); void Usr_ListDataAdms (void);
void Usr_GetAndUpdatePrefsAboutUsrList (void); void Usr_GetAndUpdatePrefsAboutUsrList (void);

View File

@ -109,6 +109,7 @@ cp -f /home/acanas/swad/swad/swad /var/www/cgi-bin/
#include "swad_notice.h" #include "swad_notice.h"
#include "swad_notification.h" #include "swad_notification.h"
#include "swad_password.h" #include "swad_password.h"
#include "swad_search.h"
#include "swad_user.h" #include "swad_user.h"
#include "swad_web_service.h" #include "swad_web_service.h"
@ -179,13 +180,15 @@ static int Svc_CheckParamsNewAccount (char *NewNicknameWithArroba, // Input
char *NewPlainPassword, // Input char *NewPlainPassword, // Input
char *NewEncryptedPassword); // Output char *NewEncryptedPassword); // Output
static void Svc_CopyListUsers (Rol_Role_t Role,struct swad__getUsersOutput *getUsersOut);
static void Svc_CopyUsrData (struct swad__user *Usr,struct UsrData *UsrDat,bool UsrIDIsVisible); static void Svc_CopyUsrData (struct swad__user *Usr,struct UsrData *UsrDat,bool UsrIDIsVisible);
static void Svc_GetListGrpsInAttendanceEvent (long AttCod,char **ListGroups); static void Svc_GetListGrpsInAttendanceEventFromDB (long AttCod,char **ListGroups);
static void Svc_GetLstGrpsSel (const char *Groups);
static int Svc_GetMyLanguage (void); static int Svc_GetMyLanguage (void);
static int Svc_sendMessageToUsr (long OriginalMsgCod,long SenderUsrCod,long ReplyUsrCod,long RecipientUsrCod,bool NotifyByEmail,const char *Subject,const char *Content); static int Svc_SendMessageToUsr (long OriginalMsgCod,long SenderUsrCod,long ReplyUsrCod,long RecipientUsrCod,bool NotifyByEmail,const char *Subject,const char *Content);
static int Svc_GetTstConfig (long CrsCod); static int Svc_GetTstConfig (long CrsCod);
static int Svc_GetNumTestQuestionsInCrs (long CrsCod); static int Svc_GetNumTestQuestionsInCrs (long CrsCod);
@ -1340,25 +1343,20 @@ int swad__getCourseInfo (struct soap *soap,
} }
/*****************************************************************************/ /*****************************************************************************/
/********************** Return students in a course **************************/ /************* Get users in a course (and optionally in groups) **************/
/*****************************************************************************/ /*****************************************************************************/
// If groupCode <= 0 ==> get users from the whole course
int swad__getUsers (struct soap *soap, int swad__getUsers (struct soap *soap,
char *wsKey,int courseCode,int groupCode,int userRole, // input char *wsKey,int courseCode,char *groups,int userRole, // input
struct swad__getUsersOutput *getUsersOut) // output struct swad__getUsersOutput *getUsersOut) // output
{ {
int ReturnCode; int ReturnCode;
char Query[512]; Rol_Role_t Role;
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumRow,NumRows;
bool UsrIDIsVisible;
Gbl.soap = soap; Gbl.soap = soap;
Gbl.WebService.Function = Svc_getUsers; Gbl.WebService.Function = Svc_getUsers;
Gbl.CurrentCrs.Crs.CrsCod = (long) courseCode; Gbl.CurrentCrs.Crs.CrsCod = (courseCode > 0) ? (long) courseCode :
Gbl.CurrentCrs.Grps.GrpCod = (long) groupCode; -1L;
/***** Check web service key *****/ /***** Check web service key *****/
if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK)
@ -1368,8 +1366,8 @@ int swad__getUsers (struct soap *soap,
"Bad web service key", "Bad web service key",
"Web service key does not exist in database"); "Web service key does not exist in database");
/***** Check course and group codes *****/ /***** Check course *****/
if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.CurrentCrs.Crs.CrsCod,Gbl.CurrentCrs.Grps.GrpCod)) != SOAP_OK) if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.CurrentCrs.Crs.CrsCod,-1L)) != SOAP_OK)
return ReturnCode; return ReturnCode;
/***** Get some of my data *****/ /***** Get some of my data *****/
@ -1382,78 +1380,194 @@ int swad__getUsers (struct soap *soap,
if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_STUDENT && if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_STUDENT &&
Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER)
return soap_receiver_fault (Gbl.soap, return soap_receiver_fault (Gbl.soap,
"Request forbidden", "Request forbidden",
"Requester must belong to course"); "Requester must belong to course");
/***** Get degree of current course *****/ /***** Get degree of current course *****/
if ((ReturnCode = Svc_GetCurrentDegCodFromCurrentCrsCod ()) != SOAP_OK) if ((ReturnCode = Svc_GetCurrentDegCodFromCurrentCrsCod ()) != SOAP_OK) // TODO: Is this necessary?
return ReturnCode; return ReturnCode;
/***** Check requested users' type *****/ /***** Check requested users' role *****/
if (userRole != 2 && // student if (userRole < 2 || // Students
userRole != 3) // teacher userRole > 3) // Teachers
{
sprintf (Gbl.Message,"Only students (user's type = %u) or teachers (user's type = %u) are allowed",
(unsigned) 2, // student
(unsigned) 3); // teacher
return soap_sender_fault (Gbl.soap, return soap_sender_fault (Gbl.soap,
"Bad requested users' type", "Bad requested users' type",
Gbl.Message); "User roles allowed are 2 (students) or 3 (teachers)");
} Role = (Rol_Role_t) userRole;
/***** Query users beloging to course or group from database *****/ /***** Create a list of groups selected *****/
if (groupCode <= 0) // Users belonging to the whole course Svc_GetLstGrpsSel (groups);
sprintf (Query,"SELECT usr_data.UsrCod" if (Gbl.CurrentCrs.Grps.LstGrpsSel.NumGrps)
" FROM crs_usr,usr_data" /***** Get list of groups types and groups in current course *****/
" WHERE crs_usr.CrsCod='%ld' AND crs_usr.UsrCod=usr_data.UsrCod AND crs_usr.Role='%d'" Grp_GetListGrpTypesInThisCrs (Grp_ONLY_GROUP_TYPES_WITH_GROUPS);
" ORDER BY usr_data.Surname1,usr_data.Surname2,usr_data.FirstName,usr_data.UsrCod",
(long) courseCode,
userRole == 2 ? (unsigned) Rol_STUDENT :
(unsigned) Rol_TEACHER);
else // Users belonging to the group
sprintf (Query,"SELECT usr_data.UsrCod"
" FROM crs_grp_usr,crs_usr,usr_data"
" WHERE crs_grp_usr.GrpCod='%ld' AND crs_grp_usr.UsrCod=crs_usr.UsrCod AND crs_grp_usr.UsrCod=usr_data.UsrCod AND crs_usr.Role='%d'"
" ORDER BY usr_data.Surname1,usr_data.Surname2,usr_data.FirstName,usr_data.UsrCod",
(long) groupCode,
userRole == 2 ? (unsigned) Rol_STUDENT :
(unsigned) Rol_TEACHER);
NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get users");
getUsersOut->numUsers = (int) NumRows; /***** Get list of users *****/
getUsersOut->usersArray.__size = (int) NumRows; Usr_GetListUsrs (Role,Sco_SCOPE_CRS);
Svc_CopyListUsers (Role,getUsersOut);
Usr_FreeUsrsList (Role);
if (NumRows == 0) if (Gbl.CurrentCrs.Grps.LstGrpsSel.NumGrps)
getUsersOut->usersArray.__ptr = NULL;
else // Users found
{ {
getUsersOut->usersArray.__ptr = soap_malloc (Gbl.soap,(getUsersOut->usersArray.__size) * sizeof (*(getUsersOut->usersArray.__ptr))); /***** Free list of groups types and groups in current course *****/
Grp_FreeListGrpTypesAndGrps ();
/***** Users' IDs are visible? *****/ /***** Free memory for list of selected groups *****/
UsrIDIsVisible = (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB == Rol_STUDENT && Grp_FreeListCodSelectedGrps ();
userRole == 2); // get students in the course
for (NumRow = 0;
NumRow < NumRows;
NumRow++)
{
/* Get next user */
row = mysql_fetch_row (mysql_res);
/* Get user's code (row[0]) */
if ((Gbl.Usrs.Other.UsrDat.UsrCod = (long) Str_ConvertStrCodToLongCod (row[0])) > 0)
/* Get user's data */
if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat))
/* Copy user's data into output structure */
Svc_CopyUsrData (&(getUsersOut->usersArray.__ptr[NumRow]),&Gbl.Usrs.Other.UsrDat,UsrIDIsVisible);
}
} }
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
return SOAP_OK; return SOAP_OK;
} }
int swad__findUsers (struct soap *soap,
char *wsKey,int courseCode,char *filter,int userRole, // input
struct swad__getUsersOutput *getUsersOut) // output
{
int ReturnCode;
char SearchQuery[Sch_MAX_LENGTH_SEARCH_QUERY+1];
Rol_Role_t Role;
bool FilterTooShort = false;
Gbl.soap = soap;
Gbl.WebService.Function = Svc_getUsers;
Gbl.CurrentCrs.Crs.CrsCod = (courseCode > 0) ? (long) courseCode :
-1L;
/***** Check web service key *****/
if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK)
return ReturnCode;
if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database
return soap_receiver_fault (Gbl.soap,
"Bad web service key",
"Web service key does not exist in database");
if (Gbl.CurrentCrs.Crs.CrsCod > 0)
/***** Check course *****/
if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.CurrentCrs.Crs.CrsCod,-1L)) != SOAP_OK)
return ReturnCode;
/***** Get some of my data *****/
if ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK)
return ReturnCode;
Gbl.Usrs.Me.Logged = true;
Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB;
if (Gbl.CurrentCrs.Crs.CrsCod > 0)
/***** Check if I am a student or teacher in the course *****/
if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_STUDENT &&
Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER)
return soap_receiver_fault (Gbl.soap,
"Request forbidden",
"Requester must belong to course");
if (Gbl.CurrentCrs.Crs.CrsCod > 0)
{
/***** Get degree of current course *****/
if ((ReturnCode = Svc_GetCurrentDegCodFromCurrentCrsCod ()) != SOAP_OK) // TODO: Is this necessary?
return ReturnCode;
}
/***** Check requested users' role *****/
if (userRole < 0 ||
userRole > 3)
return soap_sender_fault (Gbl.soap,
"Bad requested users' type",
"User roles allowed are 0 (all),1 (guests), 2 (students) or 3 (teachers)");
Role = (Rol_Role_t) userRole;
/***** Query users beloging to course or group from database *****/
strncpy (Gbl.Search.Str,filter,Sch_MAX_LENGTH_STRING_TO_FIND);
Gbl.Search.Str[Sch_MAX_LENGTH_STRING_TO_FIND] = '\0';
if (Gbl.Search.Str[0]) // Search some users
{
Gbl.Scope.Current = (Gbl.CurrentCrs.Crs.CrsCod > 0) ? Sco_SCOPE_CRS :
Sco_SCOPE_SYS;
if (Sch_BuildSearchQuery (SearchQuery,
"CONCAT_WS(' ',FirstName,Surname1,Surname2)",
NULL,NULL))
{
/***** Create temporary table with candidate users *****/
// Search is faster (aproximately x2) using temporary tables
Usr_CreateTmpTableAndSearchCandidateUsrs (SearchQuery);
/***** Search for users *****/
Usr_SearchListUsrs (Role);
Svc_CopyListUsers (Role,getUsersOut);
Usr_FreeUsrsList (Role);
/***** Drop temporary table with candidate users *****/
Usr_DropTmpTableWithCandidateUsrs ();
}
else
FilterTooShort = true;
}
else
FilterTooShort = true;
/***** Return error in filter? *****/
if (FilterTooShort)
{
getUsersOut->numUsers = -1; // < 0 ==> filter too short
getUsersOut->usersArray.__size = 0;
getUsersOut->usersArray.__ptr = NULL;
}
return SOAP_OK;
}
/*****************************************************************************/
/***************************** Copy users from list **************************/
/*****************************************************************************/
static void Svc_CopyListUsers (Rol_Role_t Role,struct swad__getUsersOutput *getUsersOut)
{
unsigned NumUsrs;
unsigned NumUsr;
struct UsrData UsrDat;
bool ICanSeeUsrID;
/***** Initialize result *****/
getUsersOut->numUsers = 0;
getUsersOut->usersArray.__size = 0;
getUsersOut->usersArray.__ptr = NULL;
NumUsrs = Gbl.Usrs.LstUsrs[Role].NumUsrs;
if (NumUsrs)
{
getUsersOut->numUsers = (int) NumUsrs;
getUsersOut->usersArray.__size = (int) NumUsrs;
getUsersOut->usersArray.__ptr = soap_malloc (Gbl.soap,(getUsersOut->usersArray.__size) * sizeof (*(getUsersOut->usersArray.__ptr)));
/***** Initialize structure with user's data *****/
Usr_UsrDataConstructor (&UsrDat);
/***** List data of users *****/
for (NumUsr = 0;
NumUsr < NumUsrs;
NumUsr++)
{
UsrDat.UsrCod = Gbl.Usrs.LstUsrs[Role].Lst[NumUsr].UsrCod;
/* Get user's data */
if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&UsrDat)) // If user's data exist...
{
UsrDat.Accepted = Gbl.Usrs.LstUsrs[Role].Lst[NumUsr].Accepted;
if (Gbl.Usrs.Me.UsrDat.UsrCod == UsrDat.UsrCod) // It's me
ICanSeeUsrID = true;
else // A user distinct than me
ICanSeeUsrID = ID_ICanSeeAnotherUsrID (&UsrDat);
/* Copy user's data into output structure */
Svc_CopyUsrData (&(getUsersOut->usersArray.__ptr[NumUsr]),&UsrDat,ICanSeeUsrID);
}
}
/***** Free memory used for user's data *****/
Usr_UsrDataDestructor (&UsrDat);
}
}
/*****************************************************************************/ /*****************************************************************************/
/********************** Return group types in a course ***********************/ /********************** Return group types in a course ***********************/
/*****************************************************************************/ /*****************************************************************************/
@ -2026,7 +2140,7 @@ int swad__getAttendanceEvents (struct soap *soap,
row[6]); row[6]);
/* Get list of groups for this attendance event */ /* Get list of groups for this attendance event */
Svc_GetListGrpsInAttendanceEvent (AttCod,&(getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].groups)); Svc_GetListGrpsInAttendanceEventFromDB (AttCod,&(getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].groups));
} }
} }
@ -2040,7 +2154,7 @@ int swad__getAttendanceEvents (struct soap *soap,
/**************** Get lists of groups of an attendance event *****************/ /**************** Get lists of groups of an attendance event *****************/
/*****************************************************************************/ /*****************************************************************************/
static void Svc_GetListGrpsInAttendanceEvent (long AttCod,char **ListGroups) static void Svc_GetListGrpsInAttendanceEventFromDB (long AttCod,char **ListGroups)
{ {
char Query[128]; char Query[128];
MYSQL_RES *mysql_res; MYSQL_RES *mysql_res;
@ -2093,9 +2207,6 @@ int swad__sendAttendanceEvent (struct soap *soap,
int ReturnCode; int ReturnCode;
struct AttendanceEvent Att; struct AttendanceEvent Att;
bool ItsANewAttEvent; bool ItsANewAttEvent;
const char *Ptr;
char LongStr[1+10+1];
unsigned NumGrp;
Gbl.soap = soap; Gbl.soap = soap;
Gbl.WebService.Function = Svc_sendAttendanceEvent; Gbl.WebService.Function = Svc_sendAttendanceEvent;
@ -2168,8 +2279,35 @@ int swad__sendAttendanceEvent (struct soap *soap,
strncpy (Att.Title,title,Att_MAX_LENGTH_ATTENDANCE_EVENT_TITLE); strncpy (Att.Title,title,Att_MAX_LENGTH_ATTENDANCE_EVENT_TITLE);
Att.Title[Att_MAX_LENGTH_ATTENDANCE_EVENT_TITLE] = '\0'; Att.Title[Att_MAX_LENGTH_ATTENDANCE_EVENT_TITLE] = '\0';
/* Create a list of groups selected */
Svc_GetLstGrpsSel (groups);
/***** Create or update attendance event *****/
if (ItsANewAttEvent)
Att_CreateAttEvent (&Att,text); // Add new attendance event to database
else
Att_UpdateAttEvent (&Att,text); // Modify existing attendance event
/***** Free memory for list of selected groups *****/
Grp_FreeListCodSelectedGrps ();
sendAttendanceEventOut->attendanceEventCode = Att.AttCod;
return SOAP_OK;
}
/*****************************************************************************/
/********************** Create a list of groups selected *********************/
/*****************************************************************************/
static void Svc_GetLstGrpsSel (const char *Groups)
{
const char *Ptr;
char LongStr[1+10+1];
unsigned NumGrp;
/***** Count number of groups *****/ /***** Count number of groups *****/
for (Ptr = groups, NumGrp = 0; for (Ptr = Groups, NumGrp = 0;
*Ptr; *Ptr;
NumGrp++) NumGrp++)
Str_GetNextStringUntilComma (&Ptr,LongStr,1+10); Str_GetNextStringUntilComma (&Ptr,LongStr,1+10);
@ -2184,7 +2322,7 @@ int swad__sendAttendanceEvent (struct soap *soap,
if ((Gbl.CurrentCrs.Grps.LstGrpsSel.GrpCod = (long *) calloc (Gbl.CurrentCrs.Grps.LstGrpsSel.NumGrps,sizeof (long))) == NULL) if ((Gbl.CurrentCrs.Grps.LstGrpsSel.GrpCod = (long *) calloc (Gbl.CurrentCrs.Grps.LstGrpsSel.NumGrps,sizeof (long))) == NULL)
Lay_ShowErrorAndExit ("Not enough memory to store the codes of the selected groups."); Lay_ShowErrorAndExit ("Not enough memory to store the codes of the selected groups.");
for (Ptr = groups, NumGrp = 0; for (Ptr = Groups, NumGrp = 0;
*Ptr; *Ptr;
) )
{ {
@ -2195,19 +2333,6 @@ int swad__sendAttendanceEvent (struct soap *soap,
} }
Gbl.CurrentCrs.Grps.LstGrpsSel.NumGrps = NumGrp; // Update number of groups Gbl.CurrentCrs.Grps.LstGrpsSel.NumGrps = NumGrp; // Update number of groups
} }
/***** Create or update attendance event *****/
if (ItsANewAttEvent)
Att_CreateAttEvent (&Att,text); // Add new attendance event to database
else
Att_UpdateAttEvent (&Att,text); // Modify existing attendance event
/***** Free memory for list of selected groups *****/
Grp_FreeListCodSelectedGrps ();
sendAttendanceEventOut->attendanceEventCode = Att.AttCod;
return SOAP_OK;
} }
/*****************************************************************************/ /*****************************************************************************/
@ -2991,7 +3116,7 @@ int swad__sendMessage (struct soap *soap,
(Gbl.Usrs.Other.UsrDat.Prefs.EmailNtfEvents & (1 << Ntf_EVENT_MESSAGE))); (Gbl.Usrs.Other.UsrDat.Prefs.EmailNtfEvents & (1 << Ntf_EVENT_MESSAGE)));
/* Send message to this user */ /* Send message to this user */
if ((ReturnCode = Svc_sendMessageToUsr ((long) messageCode,Gbl.Usrs.Me.UsrDat.UsrCod,ReplyUsrCod,Gbl.Usrs.Other.UsrDat.UsrCod,NotifyByEmail,subject,body)) != SOAP_OK) if ((ReturnCode = Svc_SendMessageToUsr ((long) messageCode,Gbl.Usrs.Me.UsrDat.UsrCod,ReplyUsrCod,Gbl.Usrs.Other.UsrDat.UsrCod,NotifyByEmail,subject,body)) != SOAP_OK)
{ {
DB_FreeMySQLResult (&mysql_res); DB_FreeMySQLResult (&mysql_res);
return ReturnCode; return ReturnCode;
@ -3013,7 +3138,7 @@ int swad__sendMessage (struct soap *soap,
/************************* Send a message to one user ************************/ /************************* Send a message to one user ************************/
/*****************************************************************************/ /*****************************************************************************/
static int Svc_sendMessageToUsr (long OriginalMsgCod,long SenderUsrCod,long ReplyUsrCod,long RecipientUsrCod,bool NotifyByEmail,const char *Subject,const char *Content) static int Svc_SendMessageToUsr (long OriginalMsgCod,long SenderUsrCod,long ReplyUsrCod,long RecipientUsrCod,bool NotifyByEmail,const char *Subject,const char *Content)
{ {
static bool MsgAlreadyInserted = false; static bool MsgAlreadyInserted = false;
static long NewMsgCod; static long NewMsgCod;