From f104cf00dec36dc41b4183d4bb33fcd83726ad75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Ca=C3=B1as=20Vargas?= Date: Wed, 29 Jun 2016 18:27:49 +0200 Subject: [PATCH] Version 15.234 --- soap/swad_web_service.h | 6 +- swad_ID.c | 3 +- swad_ID.h | 1 + swad_changelog.h | 8 +- swad_search.c | 164 ++++++++++----------- swad_search.h | 8 +- swad_user.c | 21 ++- swad_user.h | 6 +- swad_web_service.c | 309 ++++++++++++++++++++++++++++------------ 9 files changed, 330 insertions(+), 196 deletions(-) diff --git a/soap/swad_web_service.h b/soap/swad_web_service.h index c3231bcf..d68e60f1 100644 --- a/soap/swad_web_service.h +++ b/soap/swad_web_service.h @@ -410,9 +410,11 @@ int swad__getTests (char *wsKey,int courseCode,long beginTime, int swad__getTrivialQuestion (char *wsKey,char *degrees,float lowerScore,float upperScore, struct swad__getTrivialQuestionOutput *getTrivialQuestionOut); -/* List of users of a course / group */ -int swad__getUsers (char *wsKey,int courseCode,int groupCode,int userRole, +/* List of users */ +int swad__getUsers (char *wsKey,int courseCode,char *groups,int userRole, struct swad__getUsersOutput *getUsersOut); +int swad__findUsers (char *wsKey,int courseCode,char *filter,int userRole, + struct swad__getUsersOutput *getUsersOut); /* Control of attendance */ int swad__getAttendanceEvents (char *wsKey,int courseCode, diff --git a/swad_ID.c b/swad_ID.c index 407fd696..c9eaa0c4 100644 --- a/swad_ID.c +++ b/swad_ID.c @@ -70,7 +70,6 @@ typedef enum 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_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 -static bool ID_ICanSeeAnotherUsrID (struct UsrData *UsrDat) +bool ID_ICanSeeAnotherUsrID (struct UsrData *UsrDat) { /***** Check if I have permission to see another user's IDs *****/ switch (Gbl.Usrs.Me.LoggedRole) diff --git a/swad_ID.h b/swad_ID.h index be7b37b9..89ceca19 100644 --- a/swad_ID.h +++ b/swad_ID.h @@ -65,6 +65,7 @@ bool ID_CheckIfUsrIDIsValid (const char *UsrID); bool ID_CheckIfUsrIDSeemsAValidID (const char *UsrID); void ID_WriteUsrIDs (struct UsrData *UsrDat); +bool ID_ICanSeeAnotherUsrID (struct UsrData *UsrDat); void ID_PutLinkToChangeUsrIDs (void); void ID_ShowFormOthIDs (void); diff --git a/swad_changelog.h b/swad_changelog.h index 4d82c230..43cbf130 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -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: 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: 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: Add a new WS function to count the nunmber of users to return in call to function getUsers @@ -129,13 +134,14 @@ /****************************** 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 JS_FILE "swad15.226.js" // 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 /* + 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.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) diff --git a/swad_search.c b/swad_search.c index b9da1d26..3cff7378 100644 --- a/swad_search.c +++ b/swad_search.c @@ -41,11 +41,8 @@ /***************************** 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_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 *******************************/ @@ -77,8 +74,6 @@ static unsigned Sch_SearchUsrsInDB (Rol_Role_t Role); static unsigned Sch_SearchOpenDocumentsInDB (const char *RangeQuery); static unsigned Sch_SearchDocumentsInMyCoursesInDB (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); @@ -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) { + extern const char *Txt_The_search_text_must_be_longer; + static bool WarningMessageWritten = false; char SearchQuery[Sch_MAX_LENGTH_SEARCH_QUERY+1]; - /***** Check user's permission *****/ - if (Sch_CheckIfIHavePermissionToSearch ( Role == Rol_UNKNOWN ? Sch_SEARCH_USERS : - (Role == Rol_TEACHER ? Sch_SEARCH_TEACHERS : - (Role == Rol_STUDENT ? Sch_SEARCH_STUDENTS : - Sch_SEARCH_GUESTS)))) - /***** Split user string into words *****/ - if (Sch_BuildSearchQuery (SearchQuery, - "CONCAT_WS(' ',usr_data.FirstName,usr_data.Surname1,usr_data.Surname2)", - NULL,NULL)) - /***** Query database and list users found *****/ - return Usr_ListUsrsFound (Role,SearchQuery); + /***** Split user string into words *****/ + if (Sch_BuildSearchQuery (SearchQuery, + "CONCAT_WS(' ',FirstName,Surname1,Surname2)", + NULL,NULL)) + /***** Query database and list users found *****/ + return Usr_ListUsrsFound (Role,SearchQuery); + else + // Too short + if (!WarningMessageWritten) // To avoid repetitions + { + Lay_ShowAlert (Lay_WARNING,Txt_The_search_text_must_be_longer); + WarningMessageWritten = true; + } return 0; } @@ -1215,11 +1214,9 @@ static unsigned Sch_SearchMyDocumentsInDB (const char *RangeQuery) // Returns true if a valid search query is built // Returns false when no valid search query -static bool Sch_BuildSearchQuery (char *SearchQuery,const char *FieldName, - const char *CharSet,const char *Collate) +bool Sch_BuildSearchQuery (char *SearchQuery,const char *FieldName, + const char *CharSet,const char *Collate) { - extern const char *Txt_The_search_text_must_be_longer; - static bool WarningMessageWritten = false; const char *Ptr; unsigned NumWords; 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]; bool SearchWordIsValid = true; - SearchQuery[0] = '\0'; - Ptr = Gbl.Search.Str; - for (NumWords = 0; - NumWords < Sch_MAX_WORDS_IN_SEARCH && *Ptr; - NumWords++) + if (Gbl.Search.Str[0]) { - /* Get next word */ - Str_GetNextStringUntilSpace (&Ptr,SearchWords[NumWords],Sch_MAX_LENGTH_SEARCH_WORD); - - /* Is this word valid? */ - switch (Gbl.Search.WhatToSearch) - { - 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) + SearchQuery[0] = '\0'; + Ptr = Gbl.Search.Str; + for (NumWords = 0; + NumWords < Sch_MAX_WORDS_IN_SEARCH && *Ptr; + NumWords++) { - 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); - } - } + /* Get next word */ + Str_GetNextStringUntilSpace (&Ptr,SearchWords[NumWords],Sch_MAX_LENGTH_SEARCH_WORD); - /***** If search string valid? *****/ - if (LengthTotal < Sch_MIN_LENGTH_TOTAL || - MaxLengthWord < Sch_MIN_LENGTH_LONGEST_WORD) - { - // Too short - if (!WarningMessageWritten) // To avoid repetitions - { - Lay_ShowAlert (Lay_WARNING,Txt_The_search_text_must_be_longer); - WarningMessageWritten = true; + /* Is this word valid? */ + switch (Gbl.Search.WhatToSearch) + { + 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]); + 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; - } - else + + /***** If search string valid? *****/ + if (LengthTotal < Sch_MIN_LENGTH_TOTAL || + MaxLengthWord < Sch_MIN_LENGTH_LONGEST_WORD) + return false; + return true; + } + + return false; } /*****************************************************************************/ diff --git a/swad_search.h b/swad_search.h index 4e8267fe..99b2afff 100644 --- a/swad_search.h +++ b/swad_search.h @@ -31,7 +31,10 @@ /****************************** 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 *******************************/ @@ -78,4 +81,7 @@ void Sch_CtrSearch (void); void Sch_DegSearch (void); void Sch_CrsSearch (void); +bool Sch_BuildSearchQuery (char *SearchQuery,const char *FieldName, + const char *CharSet,const char *Collate); + #endif diff --git a/swad_user.c b/swad_user.c index 5be6ddf7..27d820bf 100644 --- a/swad_user.c +++ b/swad_user.c @@ -142,9 +142,6 @@ static void Usr_WriteUsrData (const char *BgColor, bool NonBreak,bool Accepted); 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_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 ***********/ /*****************************************************************************/ -static void Usr_SearchListUsrs (Rol_Role_t Role) +void Usr_SearchListUsrs (Rol_Role_t Role) { char Query[4*1024]; 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 *****************/ /*****************************************************************************/ -static void Usr_CreateTmpTableAndSearchCandidateUsrs (const char *UsrQuery) +void Usr_CreateTmpTableAndSearchCandidateUsrs (const char *SearchQuery) { char Query[16*1024]; @@ -4024,7 +4021,7 @@ static void Usr_CreateTmpTableAndSearchCandidateUsrs (const char *UsrQuery) sprintf (Query,"CREATE TEMPORARY TABLE candidate_users" " (UsrCod INT NOT NULL,UNIQUE INDEX(UsrCod)) ENGINE=MEMORY" " SELECT UsrCod FROM usr_data WHERE %s", - UsrQuery); + SearchQuery); if (mysql_query (&Gbl.mysql,Query)) DB_ExitOnMySQLError ("can not create temporary table"); } @@ -4033,7 +4030,7 @@ static void Usr_CreateTmpTableAndSearchCandidateUsrs (const char *UsrQuery) /***************** Drop temporary table with candidate users *****************/ /*****************************************************************************/ -static void Usr_DropTmpTableWithCandidateUsrs (void) +void Usr_DropTmpTableWithCandidateUsrs (void) { char Query[128]; @@ -5802,24 +5799,24 @@ void Usr_ListAllDataTchs (void) /*****************************************************************************/ // 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_users[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_PLURAL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS]; - Usr_Sex_t Sex; + unsigned NumUsrs; unsigned NumUsr; struct UsrData UsrDat; - unsigned NumUsrs; + Usr_Sex_t Sex; /***** Initialize field names *****/ Usr_SetUsrDatMainFieldNames (); /***** Create temporary table with candidate users *****/ // Search is faster (aproximately x2) using temporary tables - Usr_CreateTmpTableAndSearchCandidateUsrs (UsrQuery); + Usr_CreateTmpTableAndSearchCandidateUsrs (SearchQuery); /***** Search for users *****/ Usr_SearchListUsrs (Role); @@ -5884,7 +5881,7 @@ unsigned Usr_ListUsrsFound (Rol_Role_t Role,const char *UsrQuery) /***** Free memory for teachers list *****/ Usr_FreeUsrsList (Role); - /***** Drop temporary tables *****/ + /***** Drop temporary table with candidate users *****/ Usr_DropTmpTableWithCandidateUsrs (); return NumUsrs; diff --git a/swad_user.h b/swad_user.h index 4e27c732..85c2c672 100644 --- a/swad_user.h +++ b/swad_user.h @@ -305,6 +305,10 @@ unsigned Usr_GetNumberOfTeachersInCentre (long CtrCod); 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_FreeUsrsList (Rol_Role_t Role); bool Usr_GetIfShowBigList (unsigned NumUsrs); @@ -332,7 +336,7 @@ void Usr_ListAllDataGsts (void); void Usr_ListAllDataStds (void); void Usr_ListUsrsForSelection (Rol_Role_t Role); 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_GetAndUpdatePrefsAboutUsrList (void); diff --git a/swad_web_service.c b/swad_web_service.c index f78e3bc8..52596a8a 100644 --- a/swad_web_service.c +++ b/swad_web_service.c @@ -109,6 +109,7 @@ cp -f /home/acanas/swad/swad/swad /var/www/cgi-bin/ #include "swad_notice.h" #include "swad_notification.h" #include "swad_password.h" +#include "swad_search.h" #include "swad_user.h" #include "swad_web_service.h" @@ -179,13 +180,15 @@ static int Svc_CheckParamsNewAccount (char *NewNicknameWithArroba, // Input char *NewPlainPassword, // Input 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_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_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_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, - char *wsKey,int courseCode,int groupCode,int userRole, // input + char *wsKey,int courseCode,char *groups,int userRole, // input struct swad__getUsersOutput *getUsersOut) // output { int ReturnCode; - char Query[512]; - MYSQL_RES *mysql_res; - MYSQL_ROW row; - unsigned NumRow,NumRows; - bool UsrIDIsVisible; + Rol_Role_t Role; Gbl.soap = soap; Gbl.WebService.Function = Svc_getUsers; - Gbl.CurrentCrs.Crs.CrsCod = (long) courseCode; - Gbl.CurrentCrs.Grps.GrpCod = (long) groupCode; + Gbl.CurrentCrs.Crs.CrsCod = (courseCode > 0) ? (long) courseCode : + -1L; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) @@ -1368,8 +1366,8 @@ int swad__getUsers (struct soap *soap, "Bad web service key", "Web service key does not exist in database"); - /***** Check course and group codes *****/ - if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.CurrentCrs.Crs.CrsCod,Gbl.CurrentCrs.Grps.GrpCod)) != SOAP_OK) + /***** Check course *****/ + if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.CurrentCrs.Crs.CrsCod,-1L)) != SOAP_OK) return ReturnCode; /***** Get some of my data *****/ @@ -1382,78 +1380,194 @@ int swad__getUsers (struct soap *soap, 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"); + "Request forbidden", + "Requester must belong to course"); /***** Get degree of current course *****/ - if ((ReturnCode = Svc_GetCurrentDegCodFromCurrentCrsCod ()) != SOAP_OK) + if ((ReturnCode = Svc_GetCurrentDegCodFromCurrentCrsCod ()) != SOAP_OK) // TODO: Is this necessary? return ReturnCode; - /***** Check requested users' type *****/ - if (userRole != 2 && // student - userRole != 3) // teacher - { - sprintf (Gbl.Message,"Only students (user's type = %u) or teachers (user's type = %u) are allowed", - (unsigned) 2, // student - (unsigned) 3); // teacher + /***** Check requested users' role *****/ + if (userRole < 2 || // Students + userRole > 3) // Teachers return soap_sender_fault (Gbl.soap, "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 *****/ - if (groupCode <= 0) // Users belonging to the whole course - sprintf (Query,"SELECT usr_data.UsrCod" - " FROM crs_usr,usr_data" - " WHERE crs_usr.CrsCod='%ld' AND crs_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) 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"); + /***** Create a list of groups selected *****/ + Svc_GetLstGrpsSel (groups); + if (Gbl.CurrentCrs.Grps.LstGrpsSel.NumGrps) + /***** Get list of groups types and groups in current course *****/ + Grp_GetListGrpTypesInThisCrs (Grp_ONLY_GROUP_TYPES_WITH_GROUPS); - getUsersOut->numUsers = (int) NumRows; - getUsersOut->usersArray.__size = (int) NumRows; + /***** Get list of users *****/ + Usr_GetListUsrs (Role,Sco_SCOPE_CRS); + Svc_CopyListUsers (Role,getUsersOut); + Usr_FreeUsrsList (Role); - if (NumRows == 0) - getUsersOut->usersArray.__ptr = NULL; - else // Users found + if (Gbl.CurrentCrs.Grps.LstGrpsSel.NumGrps) { - 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? *****/ - UsrIDIsVisible = (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB == Rol_STUDENT && - 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 memory for list of selected groups *****/ + Grp_FreeListCodSelectedGrps (); } - /***** Free structure that stores the query result *****/ - DB_FreeMySQLResult (&mysql_res); - 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 ***********************/ /*****************************************************************************/ @@ -2026,7 +2140,7 @@ int swad__getAttendanceEvents (struct soap *soap, row[6]); /* 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 *****************/ /*****************************************************************************/ -static void Svc_GetListGrpsInAttendanceEvent (long AttCod,char **ListGroups) +static void Svc_GetListGrpsInAttendanceEventFromDB (long AttCod,char **ListGroups) { char Query[128]; MYSQL_RES *mysql_res; @@ -2093,9 +2207,6 @@ int swad__sendAttendanceEvent (struct soap *soap, int ReturnCode; struct AttendanceEvent Att; bool ItsANewAttEvent; - const char *Ptr; - char LongStr[1+10+1]; - unsigned NumGrp; Gbl.soap = soap; 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); 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 *****/ - for (Ptr = groups, NumGrp = 0; + for (Ptr = Groups, NumGrp = 0; *Ptr; NumGrp++) 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) Lay_ShowErrorAndExit ("Not enough memory to store the codes of the selected groups."); - for (Ptr = groups, NumGrp = 0; + for (Ptr = Groups, NumGrp = 0; *Ptr; ) { @@ -2195,19 +2333,6 @@ int swad__sendAttendanceEvent (struct soap *soap, } 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))); /* 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); return ReturnCode; @@ -3013,7 +3138,7 @@ int swad__sendMessage (struct soap *soap, /************************* 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 long NewMsgCod;