// swad_photo.c: Users' photos management /* 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-2019 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 Public 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 . */ /*****************************************************************************/ /********************************* Headers ***********************************/ /*****************************************************************************/ #define _GNU_SOURCE // For asprintf #include // For PATH_MAX #include // For NULL #include // For log10, floor, ceil, modf, sqrt... #include // For asprintf #include // For system, getenv, etc. #include // For string functions #include // For the macro WEXITSTATUS #include // For unlink #include "swad_action.h" #include "swad_box.h" #include "swad_config.h" #include "swad_database.h" #include "swad_enrolment.h" #include "swad_file.h" #include "swad_file_browser.h" #include "swad_follow.h" #include "swad_form.h" #include "swad_global.h" #include "swad_HTML.h" #include "swad_logo.h" #include "swad_parameter.h" #include "swad_photo.h" #include "swad_privacy.h" #include "swad_setting.h" #include "swad_theme.h" #include "swad_user.h" /*****************************************************************************/ /************** External global variables from others modules ****************/ /*****************************************************************************/ extern struct Globals Gbl; /*****************************************************************************/ /****************************** Public constants *****************************/ /*****************************************************************************/ /*****************************************************************************/ /***************************** Private constants *****************************/ /*****************************************************************************/ const char *Pho_StrAvgPhotoDirs[Pho_NUM_AVERAGE_PHOTO_TYPES] = { Cfg_FOLDER_DEGREE_PHOTO_MEDIAN, Cfg_FOLDER_DEGREE_PHOTO_AVERAGE, }; const char *Pho_StrAvgPhotoPrograms[Pho_NUM_AVERAGE_PHOTO_TYPES] = { Cfg_COMMAND_DEGREE_PHOTO_MEDIAN, Cfg_COMMAND_DEGREE_PHOTO_AVERAGE, }; /*****************************************************************************/ /******************************* Private types *******************************/ /*****************************************************************************/ /*****************************************************************************/ /***************************** Private variables *****************************/ /*****************************************************************************/ /*****************************************************************************/ /***************************** Private prototypes ****************************/ /*****************************************************************************/ static void Pho_PutIconToRequestRemoveMyPhoto (void); static void Pho_PutIconToRequestRemoveOtherUsrPhoto (void); static void Pho_ReqOtherUsrPhoto (void); static void Pho_ReqPhoto (const struct UsrData *UsrDat); static bool Pho_ReceivePhotoAndDetectFaces (bool ItsMe,const struct UsrData *UsrDat); static void Pho_UpdatePhoto1 (struct UsrData *UsrDat); static void Pho_UpdatePhoto2 (void); static void Pho_ClearPhotoName (long UsrCod); static long Pho_GetDegWithAvgPhotoLeastRecentlyUpdated (void); static long Pho_GetTimeAvgPhotoWasComputed (long DegCod); static long Pho_GetTimeToComputeAvgPhoto (long DegCod); static void Pho_ComputeAveragePhoto (long DegCod,Usr_Sex_t Sex,Rol_Role_t Role, Pho_AvgPhotoTypeOfAverage_t TypeOfAverage,const char *DirAvgPhotosRelPath, unsigned *NumStds,unsigned *NumStdsWithPhoto,long *TimeToComputeAvgPhotoInMicroseconds); static void Pho_PutSelectorForTypeOfAvg (void); static Pho_AvgPhotoTypeOfAverage_t Pho_GetPhotoAvgTypeFromForm (void); static void Pho_PutSelectorForHowComputePhotoSize (void); static Pho_HowComputePhotoSize_t Pho_GetHowComputePhotoSizeFromForm (void); static void Pho_PutSelectorForHowOrderDegrees (void); static Pho_HowOrderDegrees_t Pho_GetHowOrderDegreesFromForm (void); static void Pho_PutIconToPrintDegreeStats (void); static void Pho_PutLinkToPrintViewOfDegreeStatsParams (void); static void Pho_PutLinkToCalculateDegreeStats (void); static void Pho_GetMaxStdsPerDegree (void); static void Pho_ShowOrPrintClassPhotoDegrees (Pho_AvgPhotoSeeOrPrint_t SeeOrPrint); static void Pho_ShowOrPrintListDegrees (Pho_AvgPhotoSeeOrPrint_t SeeOrPrint); static unsigned long Pho_BuildQueryOfDegrees (MYSQL_RES **mysql_res); static void Pho_GetNumStdsInDegree (long DegCod,Usr_Sex_t Sex,int *NumStds,int *NumStdsWithPhoto); static void Pho_UpdateDegStats (long DegCod,Usr_Sex_t Sex,unsigned NumStds,unsigned NumStdsWithPhoto,long TimeToComputeAvgPhoto); static void Pho_ShowDegreeStat (int NumStds,int NumStdsWithPhoto); static void Pho_ShowDegreeAvgPhotoAndStat (struct Degree *Deg, Pho_AvgPhotoSeeOrPrint_t SeeOrPrint, Usr_Sex_t Sex, int NumStds,int NumStdsWithPhoto); static void Pho_ComputePhotoSize (int NumStds,int NumStdsWithPhoto,unsigned *PhotoWidth,unsigned *PhotoHeight); /*****************************************************************************/ /************** Check if I can change the photo of another user **************/ /*****************************************************************************/ bool Pho_ICanChangeOtherUsrPhoto (const struct UsrData *UsrDat) { bool ItsMe = Usr_ItsMe (UsrDat->UsrCod); if (ItsMe) return true; /* Check if I have permission to change user's photo */ switch (Gbl.Usrs.Me.Role.Logged) { case Rol_TCH: /* A teacher can change the photo of confirmed students */ if (UsrDat->Roles.InCurrentCrs.Role == Rol_STD && // A student UsrDat->Accepted) // who accepted registration return true; return false; case Rol_DEG_ADM: case Rol_CTR_ADM: case Rol_INS_ADM: case Rol_SYS_ADM: return Usr_ICanEditOtherUsr (UsrDat); default: return false; } } /*****************************************************************************/ /********** Put a link to the action used to request user's photo ************/ /*****************************************************************************/ void Pho_PutIconToChangeUsrPhoto (void) { extern const char *Txt_Change_photo; extern const char *Txt_Upload_photo; bool PhotoExists; char PhotoURL[PATH_MAX + 1]; const char *TitleText; Act_Action_t NextAction; bool ItsMe = Usr_ItsMe (Gbl.Record.UsrDat->UsrCod); /***** Link for changing / uploading the photo *****/ if (ItsMe) { TitleText = Gbl.Usrs.Me.MyPhotoExists ? Txt_Change_photo : Txt_Upload_photo; Lay_PutContextualLinkOnlyIcon (ActReqMyPho,NULL,NULL, "camera.svg", TitleText); } else // Not me if (Pho_ICanChangeOtherUsrPhoto (Gbl.Record.UsrDat)) { PhotoExists = Pho_BuildLinkToPhoto (Gbl.Record.UsrDat,PhotoURL); TitleText = PhotoExists ? Txt_Change_photo : Txt_Upload_photo; switch (Gbl.Record.UsrDat->Roles.InCurrentCrs.Role) { case Rol_STD: NextAction = ActReqStdPho; break; case Rol_NET: case Rol_TCH: NextAction = ActReqTchPho; break; default: // Guest, user or admin NextAction = ActReqOthPho; break; } Lay_PutContextualLinkOnlyIcon (NextAction,NULL, Rec_PutParamUsrCodEncrypted, "camera.svg", TitleText); } } /*****************************************************************************/ /************** Put a link to request the removal of my photo ****************/ /*****************************************************************************/ static void Pho_PutIconToRequestRemoveMyPhoto (void) { extern const char *Txt_Remove_photo; /***** Link to request the removal of my photo *****/ if (Gbl.Usrs.Me.MyPhotoExists) Lay_PutContextualLinkOnlyIcon (ActReqRemMyPho,NULL,NULL, "trash.svg", Txt_Remove_photo); } /*****************************************************************************/ /********** Put a link to request the removal of a user's photo **************/ /*****************************************************************************/ static void Pho_PutIconToRequestRemoveOtherUsrPhoto (void) { extern const char *Txt_Remove_photo; char PhotoURL[PATH_MAX + 1]; bool PhotoExists; Act_Action_t NextAction; /***** Link to request the removal of another user's photo *****/ PhotoExists = Pho_BuildLinkToPhoto (&Gbl.Usrs.Other.UsrDat,PhotoURL); if (PhotoExists) { switch (Gbl.Usrs.Other.UsrDat.Roles.InCurrentCrs.Role) { case Rol_STD: NextAction = ActReqRemStdPho; break; case Rol_NET: case Rol_TCH: NextAction = ActReqRemTchPho; break; default: // Guest, user or admin NextAction = ActReqRemOthPho; break; } Lay_PutContextualLinkOnlyIcon (NextAction,NULL, Usr_PutParamOtherUsrCodEncrypted, "trash.svg", Txt_Remove_photo); } } /*****************************************************************************/ /************************ Form for sending my photo **************************/ /*****************************************************************************/ void Pho_ReqMyPhoto (void) { /***** Show the form for sending the photo *****/ Pho_ReqPhoto (&Gbl.Usrs.Me.UsrDat); /***** Show my record and other data *****/ Rec_ShowMySharedRecordAndMore (); } /*****************************************************************************/ /******************* Form for sending other user's photo *********************/ /*****************************************************************************/ static void Pho_ReqOtherUsrPhoto (void) { /***** Show the form to send another user's photo *****/ Pho_ReqPhoto (&Gbl.Usrs.Other.UsrDat); /***** Show another user's record card *****/ Rec_ShowPublicSharedRecordOtherUsr (); } /*****************************************************************************/ /****************** Show a form for sending an user's photo ******************/ /*****************************************************************************/ static void Pho_ReqPhoto (const struct UsrData *UsrDat) { extern const char *Hlp_PROFILE_Photo; extern const char *The_ClassFormInBox[The_NUM_THEMES]; extern const char *Txt_Photo; extern const char *Txt_You_can_send_a_file_with_an_image_in_JPEG_format_; extern const char *Txt_File_with_the_photo; extern const char *Txt_Upload_photo; bool ItsMe = Usr_ItsMe (UsrDat->UsrCod); Act_Action_t NextAction; /***** Begin box *****/ Box_BoxBegin (NULL,Txt_Photo,ItsMe ? Pho_PutIconToRequestRemoveMyPhoto : Pho_PutIconToRequestRemoveOtherUsrPhoto, Hlp_PROFILE_Photo,Box_NOT_CLOSABLE); /***** Begin form *****/ if (ItsMe) Frm_StartForm (ActDetMyPho); else { switch (Gbl.Usrs.Other.UsrDat.Roles.InCurrentCrs.Role) { case Rol_STD: NextAction = ActDetStdPho; break; case Rol_NET: case Rol_TCH: NextAction = ActDetTchPho; break; default: // Guest, user or admin NextAction = ActDetOthPho; break; } Frm_StartForm (NextAction); Usr_PutParamUsrCodEncrypted (UsrDat->EncryptedUsrCod); } /***** Show help message *****/ Ale_ShowAlert (Ale_INFO,Txt_You_can_send_a_file_with_an_image_in_JPEG_format_); /***** Form to upload photo *****/ fprintf (Gbl.F.Out,"