// swad_follow.c: user's followers and followed /* 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 . */ /*****************************************************************************/ /*********************************** Headers *********************************/ /*****************************************************************************/ #define _GNU_SOURCE // For asprintf #include // For boolean type #include // For asprintf #include // For string functions #include "swad_action_list.h" #include "swad_alert.h" #include "swad_box.h" #include "swad_database.h" #include "swad_error.h" #include "swad_figure.h" #include "swad_follow.h" #include "swad_follow_database.h" #include "swad_form.h" #include "swad_global.h" #include "swad_HTML.h" #include "swad_notification.h" #include "swad_notification_database.h" #include "swad_parameter.h" #include "swad_photo.h" #include "swad_privacy.h" #include "swad_profile.h" #include "swad_user.h" /*****************************************************************************/ /***************************** Private constants *****************************/ /*****************************************************************************/ #define Fol_NUM_COLUMNS_FOLLOW 3 #define Fol_FOLLOW_SECTION_ID "follow_section" /*****************************************************************************/ /************** External global variables from others modules ****************/ /*****************************************************************************/ extern struct Globals Gbl; /*****************************************************************************/ /***************************** Private prototypes ****************************/ /*****************************************************************************/ static void Fol_PutIconsWhoToFollow (__attribute__((unused)) void *Args); static void Fol_PutIconToUpdateWhoToFollow (void); static void Fol_ShowNumberOfFollowingOrFollowers (const struct Usr_Data *UsrDat, unsigned NumUsrs, Act_Action_t Action, const char *Title); static void Fol_ListFollowingUsr (struct Usr_Data *UsrDat); static void Fol_ListFollowersUsr (struct Usr_Data *UsrDat); static void Fol_ShowFollowedOrFollower (struct Usr_Data *UsrDat); static void Fol_WriteRowUsrToFollowOnRightColumn (struct Usr_Data *UsrDat); static void Fol_PutInactiveIconToFollowUnfollow (void); static void Fol_PutIconToFollow (const char EncryptedUsrCod[Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64 + 1]); static void Fol_PutIconToUnfollow (const char EncryptedUsrCod[Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64 + 1]); static void Fol_RequestFollowUsrs (Act_Action_t NextAction); static void Fol_RequestUnfollowUsrs (Act_Action_t NextAction); static void Fol_PutParSelectedUsrsCods (void *SelectedUsrs); static void Fol_GetFollowedFromSelectedUsrs (unsigned *NumFollowed, unsigned *NumNotFollowed); static void Fol_FollowUsr (const struct Usr_Data *UsrDat); static void Fol_UnfollowUsr (const struct Usr_Data *UsrDat); /*****************************************************************************/ /********************** Put link to show users to follow **********************/ /*****************************************************************************/ void Fol_PutLinkWhoToFollow (void) { extern const char *Txt_Who_to_follow; Lay_PutContextualLinkIconText (ActSeeSocPrf,NULL, NULL,NULL, "user-plus.svg",Ico_BLACK, Txt_Who_to_follow,NULL); } /*****************************************************************************/ /****************** Show several users to follow on main zone ****************/ /*****************************************************************************/ #define Fol_MAX_USRS_TO_FOLLOW_MAIN_ZONE (Fol_NUM_COLUMNS_FOLLOW * 3) void Fol_SuggestUsrsToFollowOnMainZone (void) { extern const char *Hlp_START_Profiles_who_to_follow; extern const char *Txt_Who_to_follow; extern const char *Txt_No_user_to_whom_you_can_follow_Try_again_later; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumUsrs; unsigned NumUsr; struct Usr_Data UsrDat; /***** Contextual menu *****/ Mnu_ContextMenuBegin (); Prf_PutLinkMyPublicProfile (); // My public profile Prf_PutLinkReqAnotherUsrProfile (); // Request another user's profile Mnu_ContextMenuEnd (); /***** Get users *****/ if ((NumUsrs = Fol_DB_GetUsrsToFollow (Fol_MAX_USRS_TO_FOLLOW_MAIN_ZONE, Fol_SUGGEST_ANY_USER, &mysql_res))) { /***** Begin box *****/ Box_BoxBegin (Txt_Who_to_follow,Fol_PutIconsWhoToFollow,NULL, Hlp_START_Profiles_who_to_follow,Box_NOT_CLOSABLE); /***** Initialize structure with user's data *****/ Usr_UsrDataConstructor (&UsrDat); /***** List users *****/ for (NumUsr = 0; NumUsr < NumUsrs; NumUsr++) { /***** Get user *****/ row = mysql_fetch_row (mysql_res); UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0]); /***** Show user *****/ if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&UsrDat, Usr_DONT_GET_PREFS, Usr_DONT_GET_ROLE_IN_CRS)) Fol_ShowFollowedOrFollower (&UsrDat); } /***** Free memory used for user's data *****/ Usr_UsrDataDestructor (&UsrDat); /***** End box *****/ Box_BoxEnd (); } else Ale_ShowAlert (Ale_INFO,Txt_No_user_to_whom_you_can_follow_Try_again_later); /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); } /*****************************************************************************/ /**************** Show several users to follow on right column ***************/ /*****************************************************************************/ #define Fol_MAX_USRS_TO_FOLLOW_RIGHT_COLUMN 3 void Fol_SuggestUsrsToFollowOnRightColumn (void) { extern const char *Txt_No_user_to_whom_you_can_follow_Try_again_later; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumUsrs; unsigned NumUsr; struct Usr_Data UsrDat; /***** Get users *****/ if ((NumUsrs = Fol_DB_GetUsrsToFollow (Fol_MAX_USRS_TO_FOLLOW_RIGHT_COLUMN, Fol_SUGGEST_ONLY_USERS_WITH_PHOTO, &mysql_res))) { /***** Initialize structure with user's data *****/ Usr_UsrDataConstructor (&UsrDat); /***** Begin table *****/ HTM_TABLE_Begin (NULL); /***** List users *****/ for (NumUsr = 0; NumUsr < NumUsrs; NumUsr++) { /***** Get user *****/ row = mysql_fetch_row (mysql_res); /* Get user's code (row[0]) */ UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0]); /***** Show user *****/ if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&UsrDat, Usr_DONT_GET_PREFS, Usr_DONT_GET_ROLE_IN_CRS)) Fol_WriteRowUsrToFollowOnRightColumn (&UsrDat); } /***** End table *****/ HTM_TABLE_End (); /***** Free memory used for user's data *****/ Usr_UsrDataDestructor (&UsrDat); /***** Link to suggest more users to follow *****/ Lay_PutContextualLinkOnlyIcon (ActSeeSocPrf,NULL, NULL,NULL, "ellipsis-h.svg",Ico_BLACK); } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); } /*****************************************************************************/ /****************** Put contextual icons in "who to follow" ******************/ /*****************************************************************************/ static void Fol_PutIconsWhoToFollow (__attribute__((unused)) void *Args) { /***** Put icon to update who to follow *****/ Fol_PutIconToUpdateWhoToFollow (); /***** Put icon to show a figure *****/ Fig_PutIconToShowFigure (Fig_FOLLOW); } /*****************************************************************************/ /********************* Put icon to update who to follow **********************/ /*****************************************************************************/ static void Fol_PutIconToUpdateWhoToFollow (void) { Ico_PutContextualIconToUpdate (ActSeeSocPrf,NULL, NULL,NULL); } /*****************************************************************************/ /*************************** Get number of followed **************************/ /*****************************************************************************/ void Fol_FlushCacheFollow (void) { Gbl.Cache.Follow.Valid = false; } void Fol_GetNumFollow (long UsrCod, unsigned *NumFollowing,unsigned *NumFollowers) { /***** 1. Fast check: trivial cases *****/ if (UsrCod <= 0) { *NumFollowing = *NumFollowers = 0; return; } /***** 2. Fast check: Is number of following already calculated? *****/ if (Gbl.Cache.Follow.Valid && UsrCod == Gbl.Cache.Follow.UsrCod) { *NumFollowing = Gbl.Cache.Follow.NumFollowing; *NumFollowers = Gbl.Cache.Follow.NumFollowers; return; } /***** 3. Slow check: Get number of following/followers from database *****/ Gbl.Cache.Follow.UsrCod = UsrCod; *NumFollowing = Gbl.Cache.Follow.NumFollowing = Fol_DB_GetNumFollowing (UsrCod); *NumFollowers = Gbl.Cache.Follow.NumFollowers = Fol_DB_GetNumFollowers (UsrCod); Gbl.Cache.Follow.Valid = true; } /*****************************************************************************/ /**************** Show following and followers of a user *********************/ /*****************************************************************************/ void Fol_ShowFollowingAndFollowers (const struct Usr_Data *UsrDat, unsigned NumFollowing,unsigned NumFollowers, bool UsrFollowsMe,bool IFollowUsr) { extern const char *Txt_FOLLOWS_YOU; extern const char *Txt_Following; extern const char *Txt_Followers; /***** Begin section *****/ HTM_SECTION_Begin (Fol_FOLLOW_SECTION_ID); /***** Followed users *****/ HTM_DIV_Begin ("id=\"following_side\""); HTM_DIV_Begin ("class=\"FOLLOW_SIDE\""); /* User follows me? */ HTM_DIV_Begin ("id=\"follows_me\" class=\"DAT_LIGHT_%s\"", The_GetSuffix ()); if (UsrFollowsMe) HTM_Txt (Txt_FOLLOWS_YOU); HTM_DIV_End (); /* Number of followed */ Fol_ShowNumberOfFollowingOrFollowers (UsrDat, NumFollowing, ActSeeFlg,Txt_Following); /* End following side */ HTM_DIV_End (); HTM_DIV_End (); /***** Followers *****/ HTM_DIV_Begin ("id=\"followers_side\""); HTM_DIV_Begin ("class=\"FOLLOW_SIDE\""); /* Number of followers */ Fol_ShowNumberOfFollowingOrFollowers (UsrDat, NumFollowers, ActSeeFlr,Txt_Followers); /* I follow user? */ HTM_DIV_Begin ("id=\"follow_usr\""); if (Gbl.Usrs.Me.Logged && // Logged Usr_ItsMe (UsrDat->UsrCod) == Usr_OTHER) // Not me! { Frm_BeginForm (IFollowUsr ? ActUnfUsr : ActFolUsr); Usr_PutParUsrCodEncrypted (UsrDat->EnUsrCod); HTM_INPUT_IMAGE (Cfg_URL_ICON_PUBLIC, IFollowUsr ? "user-check.svg" : "user-plus.svg", Act_GetActionText (IFollowUsr ? ActUnfUsr : ActFolUsr), "class=\"ICO_HIGHLIGHT ICO40x40\""); Frm_EndForm (); } HTM_DIV_End (); /* End followers side */ HTM_DIV_End (); HTM_DIV_End (); /***** End section *****/ HTM_SECTION_End (); } /*****************************************************************************/ /**************** Show following and followers of a user *********************/ /*****************************************************************************/ static void Fol_ShowNumberOfFollowingOrFollowers (const struct Usr_Data *UsrDat, unsigned NumUsrs, Act_Action_t Action, const char *Title) { /***** Begin container *****/ HTM_DIV_Begin ("class=\"FOLLOW_BOX %s\"", (Gbl.Action.Act == Action) ? "FOLLOW_NUM_B" : "FOLLOW_NUM"); /***** Number *****/ if (NumUsrs) { /* Form to list users */ Frm_BeginFormAnchor (Action,Fol_FOLLOW_SECTION_ID); Usr_PutParUsrCodEncrypted (UsrDat->EnUsrCod); HTM_BUTTON_Submit_Begin (Title,"class=\"BT_LINK\""); } HTM_Unsigned (NumUsrs); if (NumUsrs) { HTM_BUTTON_End (); Frm_EndForm (); } /***** Text *****/ HTM_DIV_Begin ("class=\"FORM_OUT_%s%s\"", The_GetSuffix (), (Gbl.Action.Act == Action) ? " BOLD" : ""); if (NumUsrs) { /* Form to list users */ Frm_BeginFormAnchor (Action,Fol_FOLLOW_SECTION_ID); Usr_PutParUsrCodEncrypted (UsrDat->EnUsrCod); HTM_BUTTON_Submit_Begin (Title,"class=\"BT_LINK\""); } HTM_Txt (Title); if (NumUsrs) { HTM_BUTTON_End (); Frm_EndForm (); } HTM_DIV_End (); /***** End container *****/ HTM_DIV_End (); } /*****************************************************************************/ /***************************** List followed users ***************************/ /*****************************************************************************/ void Fol_ListFollowing (void) { /***** Get user to view user he/she follows *****/ Usr_GetParOtherUsrCodEncryptedAndGetListIDs (); if (Gbl.Usrs.Other.UsrDat.UsrCod > 0) { if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat, Usr_DONT_GET_PREFS, Usr_DONT_GET_ROLE_IN_CRS)) Fol_ListFollowingUsr (&Gbl.Usrs.Other.UsrDat); else Ale_ShowAlertUserNotFoundOrYouDoNotHavePermission (); } else // If user not specified, view my profile Fol_ListFollowingUsr (&Gbl.Usrs.Me.UsrDat); } static void Fol_ListFollowingUsr (struct Usr_Data *UsrDat) { extern const char *Txt_Following; MYSQL_RES *mysql_res; unsigned NumUsrs; unsigned NumUsr; struct Usr_Data FollowingUsrDat; /***** Show user's profile *****/ if (Prf_ShowUsrProfile (UsrDat)) { /***** Get list of following *****/ NumUsrs = Fol_DB_GetListFollowing (UsrDat->UsrCod,&mysql_res); if (NumUsrs) { /***** Initialize structure with user's data *****/ Usr_UsrDataConstructor (&FollowingUsrDat); /***** Begin box *****/ Box_BoxBegin (Txt_Following,NULL,NULL,NULL,Box_NOT_CLOSABLE); for (NumUsr = 0; NumUsr < NumUsrs; NumUsr++) { /***** Get user's code *****/ FollowingUsrDat.UsrCod = DB_GetNextCode (mysql_res); /***** Show user *****/ if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&FollowingUsrDat, Usr_DONT_GET_PREFS, Usr_DONT_GET_ROLE_IN_CRS)) Fol_ShowFollowedOrFollower (&FollowingUsrDat); } /***** End box *****/ Box_BoxEnd (); /***** Free memory used for user's data *****/ Usr_UsrDataDestructor (&FollowingUsrDat); } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); } else Ale_ShowAlertUserNotFoundOrYouDoNotHavePermission (); } /*****************************************************************************/ /******************************* List followers ******************************/ /*****************************************************************************/ void Fol_ListFollowers (void) { /***** Get user to view user he/she follows *****/ Usr_GetParOtherUsrCodEncryptedAndGetListIDs (); if (Gbl.Usrs.Other.UsrDat.UsrCod > 0) { if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat, Usr_DONT_GET_PREFS, Usr_DONT_GET_ROLE_IN_CRS)) Fol_ListFollowersUsr (&Gbl.Usrs.Other.UsrDat); else Ale_ShowAlertUserNotFoundOrYouDoNotHavePermission (); } else // If user not specified, view my profile Fol_ListFollowersUsr (&Gbl.Usrs.Me.UsrDat); } static void Fol_ListFollowersUsr (struct Usr_Data *UsrDat) { extern const char *Txt_Followers; MYSQL_RES *mysql_res; unsigned NumUsrs; unsigned NumUsr; struct Usr_Data FollowerUsrDat; /***** Show user's profile *****/ if (Prf_ShowUsrProfile (UsrDat)) { /***** Get list of followers *****/ if ((NumUsrs = Fol_DB_GetListFollowers (UsrDat->UsrCod,&mysql_res))) { /***** Initialize structure with user's data *****/ Usr_UsrDataConstructor (&FollowerUsrDat); /***** Begin box and table *****/ Box_BoxBegin (Txt_Followers,NULL,NULL,NULL,Box_NOT_CLOSABLE); for (NumUsr = 0; NumUsr < NumUsrs; NumUsr++) { /***** Get user's code *****/ FollowerUsrDat.UsrCod = DB_GetNextCode (mysql_res); /***** Show user *****/ if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&FollowerUsrDat, Usr_DONT_GET_PREFS, Usr_DONT_GET_ROLE_IN_CRS)) Fol_ShowFollowedOrFollower (&FollowerUsrDat); } /***** End box *****/ Box_BoxEnd (); /***** Free memory used for user's data *****/ Usr_UsrDataDestructor (&FollowerUsrDat); } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); /***** If it's me, mark possible notification as seen *****/ if (Usr_ItsMe (UsrDat->UsrCod) == Usr_ME) Ntf_DB_MarkNotifsAsSeen (Ntf_EVENT_FOLLOWER); } else Ale_ShowAlertUserNotFoundOrYouDoNotHavePermission (); } /*****************************************************************************/ /************************* Show followed or follower *************************/ /*****************************************************************************/ static void Fol_ShowFollowedOrFollower (struct Usr_Data *UsrDat) { extern const char *Txt_Another_user_s_profile; static const char *ClassPhoto[PhoSha_NUM_SHAPES] = { [PhoSha_SHAPE_CIRCLE ] = "PHOTOC60x80", [PhoSha_SHAPE_ELLIPSE ] = "PHOTOE60x80", [PhoSha_SHAPE_OVAL ] = "PHOTOO60x80", [PhoSha_SHAPE_RECTANGLE] = "PHOTOR60x80", }; Usr_Can_t ICanView = Pri_CheckIfICanView (UsrDat->BaPrfVisibility,UsrDat); HTM_DIV_Begin ("class=\"FOLLOW_USR\""); /***** Show user's photo *****/ HTM_DIV_Begin ("class=\"FOLLOW_PHOTO\""); if (ICanView == Usr_CAN) Pho_ShowUsrPhotoIfAllowed (UsrDat, ClassPhoto[Gbl.Prefs.PhotoShape],Pho_ZOOM); HTM_DIV_End (); /***** Show user's name and icon to follow/unfollow *****/ HTM_DIV_Begin ("class=\"FOLLOW_TXT\""); if (ICanView == Usr_CAN) { /* Put form to go to public profile */ Frm_BeginForm (ActSeeOthPubPrf); Usr_PutParUsrCodEncrypted (UsrDat->EnUsrCod); HTM_DIV_Begin ("class=\"LT FOLLOW_TXT_NAME DAT_%s\"", // Limited width The_GetSuffix ()); HTM_BUTTON_Submit_Begin (Txt_Another_user_s_profile, "class=\"BT_LINK LT\""); Usr_WriteFirstNameBRSurnames (UsrDat); HTM_BUTTON_End (); HTM_DIV_End (); Frm_EndForm (); } if (!Gbl.Usrs.Me.Logged || // Not logged Usr_ItsMe (UsrDat->UsrCod) == Usr_ME) // It's me /* Inactive icon to follow/unfollow */ Fol_PutInactiveIconToFollowUnfollow (); else // It's not me { /* Put form to follow / unfollow */ if (Fol_DB_CheckUsrIsFollowerOf (Gbl.Usrs.Me.UsrDat.UsrCod, UsrDat->UsrCod)) // I follow user /* Form to unfollow */ Fol_PutIconToUnfollow (UsrDat->EnUsrCod); else if (ICanView == Usr_CAN) // I do not follow this user and I can follow /* Form to follow */ Fol_PutIconToFollow (UsrDat->EnUsrCod); } HTM_DIV_End (); HTM_DIV_End (); } /*****************************************************************************/ /********************* Write the name of a connected user ********************/ /*****************************************************************************/ static void Fol_WriteRowUsrToFollowOnRightColumn (struct Usr_Data *UsrDat) { extern const char *Txt_Another_user_s_profile; static const char *ClassPhoto[PhoSha_NUM_SHAPES] = { [PhoSha_SHAPE_CIRCLE ] = "PHOTOC21x28", [PhoSha_SHAPE_ELLIPSE ] = "PHOTOE21x28", [PhoSha_SHAPE_OVAL ] = "PHOTOO21x28", [PhoSha_SHAPE_RECTANGLE] = "PHOTOR21x28", }; Usr_Can_t ICanView = Pri_CheckIfICanView (UsrDat->BaPrfVisibility,UsrDat); /***** Show user's photo *****/ HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"CON_PHOTO %s\"",The_GetColorRows ()); if (ICanView == Usr_CAN) Pho_ShowUsrPhotoIfAllowed (UsrDat, ClassPhoto[Gbl.Prefs.PhotoShape],Pho_ZOOM); HTM_TD_End (); /***** User's name *****/ HTM_TD_Begin ("class=\"CON_NAME_FOLLOW %s\"", The_GetColorRows ()); if (ICanView == Usr_CAN) { /* Put form to go to public profile */ Frm_BeginForm (ActSeeOthPubPrf); Usr_PutParUsrCodEncrypted (UsrDat->EnUsrCod); HTM_DIV_Begin ("class=\"CON_NAME_FOLLOW CON_CRS LT\""); // Limited width HTM_BUTTON_Submit_Begin (Txt_Another_user_s_profile, "class=\"LT BT_LINK\""); Usr_WriteFirstNameBRSurnames (UsrDat); HTM_BUTTON_End (); HTM_DIV_End (); Frm_EndForm (); } HTM_TD_End (); /***** Icon to follow *****/ HTM_TD_Begin ("class=\"CON_ICON_FOLLOW RM %s\"", The_GetColorRows ()); if (!Gbl.Usrs.Me.Logged || // Not logged Usr_ItsMe (UsrDat->UsrCod) == Usr_ME) // It's me /* Inactive icon to follow/unfollow */ Fol_PutInactiveIconToFollowUnfollow (); else // It's not me { /* Put form to follow / unfollow */ if (Fol_DB_CheckUsrIsFollowerOf (Gbl.Usrs.Me.UsrDat.UsrCod, UsrDat->UsrCod)) // I follow user /* Form to unfollow */ Fol_PutIconToUnfollow (UsrDat->EnUsrCod); else if (ICanView == Usr_CAN) // I do not follow this user and I can follow /* Form to follow */ Fol_PutIconToFollow (UsrDat->EnUsrCod); } HTM_TD_End (); HTM_TR_End (); The_ChangeRowColor (); } /*****************************************************************************/ /************* Put inactive icon to follow/unfollow another user *************/ /*****************************************************************************/ static void Fol_PutInactiveIconToFollowUnfollow (void) { /***** Inactive icon to follow/unfollow *****/ HTM_DIV_Begin ("class=\"FOLLOW_TXT_ICO ICO_HIDDEN\""); Ico_PutIcon ("user.svg",Ico_BLACK,"","ICO16x16"); HTM_DIV_End (); } /*****************************************************************************/ /*********************** Put icon to follow another user *********************/ /*****************************************************************************/ static void Fol_PutIconToFollow (const char EncryptedUsrCod[Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64 + 1]) { Frm_BeginForm (ActFolUsr); Usr_PutParUsrCodEncrypted (EncryptedUsrCod); HTM_INPUT_IMAGE (Cfg_URL_ICON_PUBLIC,"user-plus.svg", Act_GetActionText (ActFolUsr), "class=\"FOLLOW_TXT_ICO ICO16x16 ICO_%s_%s ICO_HIGHLIGHT\"", Ico_GetPreffix (Ico_BLACK),The_GetSuffix ()); Frm_EndForm (); } /*****************************************************************************/ /********************** Put icon to unfollow another user ********************/ /*****************************************************************************/ static void Fol_PutIconToUnfollow (const char EncryptedUsrCod[Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64 + 1]) { Frm_BeginForm (ActUnfUsr); Usr_PutParUsrCodEncrypted (EncryptedUsrCod); HTM_INPUT_IMAGE (Cfg_URL_ICON_PUBLIC,"user-check.svg", Act_GetActionText (ActUnfUsr), "class=\"FOLLOW_TXT_ICO ICO_HIGHLIGHT ICO16x16\""); Frm_EndForm (); } /*****************************************************************************/ /***************************** Follow another user ***************************/ /*****************************************************************************/ void Fol_FollowUsr1 (void) { /***** Get user to be followed *****/ if (Usr_GetParOtherUsrCodEncryptedAndGetUsrData ()) { // Follow only if I do not follow him/her if (!Fol_DB_CheckUsrIsFollowerOf (Gbl.Usrs.Me.UsrDat.UsrCod, Gbl.Usrs.Other.UsrDat.UsrCod)) Fol_FollowUsr (&Gbl.Usrs.Other.UsrDat); Ale_CreateAlert (Ale_SUCCESS,NULL,""); // Txt not used } else Ale_CreateAlertUserNotFoundOrYouDoNotHavePermission (); } void Fol_FollowUsr2 (void) { if (Ale_GetTypeOfLastAlert () == Ale_SUCCESS) { /***** Show user's profile again *****/ if (!Prf_ShowUsrProfile (&Gbl.Usrs.Other.UsrDat)) /* 1) I had permission to follow the user and I've just follow him/her 2) User restricted permission, so now I can not view his/her profile 3) Now I can not view his/her profile ==> show users I follow */ Fol_ListFollowingUsr (&Gbl.Usrs.Me.UsrDat); // List users I follow } else Ale_ShowAlerts (NULL); } /*****************************************************************************/ /***************************** Unfollow another user *************************/ /*****************************************************************************/ void Fol_UnfollowUsr1 (void) { /***** Get user to be unfollowed *****/ if (Usr_GetParOtherUsrCodEncryptedAndGetUsrData ()) { // Unfollow only if I follow him/her if (Fol_DB_CheckUsrIsFollowerOf (Gbl.Usrs.Me.UsrDat.UsrCod, Gbl.Usrs.Other.UsrDat.UsrCod)) Fol_UnfollowUsr (&Gbl.Usrs.Other.UsrDat); Ale_CreateAlert (Ale_SUCCESS,NULL,""); // Txt not used } else Ale_CreateAlertUserNotFoundOrYouDoNotHavePermission (); } void Fol_UnfollowUsr2 (void) { /***** Get user to be unfollowed *****/ if (Ale_GetTypeOfLastAlert () == Ale_SUCCESS) { /***** Show user's profile again *****/ if (!Prf_ShowUsrProfile (&Gbl.Usrs.Other.UsrDat)) // I can not view user's profile /* 1) I followed a user when I had permission 2) User restricted permission, so now I can not view his/her profile 3) Now I can not view his/her profile ==> show users I follow */ Fol_ListFollowingUsr (&Gbl.Usrs.Me.UsrDat); // List users I follow } else Ale_ShowAlerts (NULL); } /*****************************************************************************/ /***************** Request follow/unfollow several users *********************/ /*****************************************************************************/ void Fol_ReqFollowStds (void) { Fol_RequestFollowUsrs (ActFolSevStd); } void Fol_ReqFollowTchs (void) { Fol_RequestFollowUsrs (ActFolSevTch); } static void Fol_RequestFollowUsrs (Act_Action_t NextAction) { extern const char *Txt_Follow; extern const char *Txt_Do_you_want_to_follow_the_selected_user_whom_you_do_not_follow_yet; extern const char *Txt_Do_you_want_to_follow_the_X_selected_users_whom_you_do_not_follow_yet; unsigned NumFollowed; unsigned NumNotFollowed; // List of selected users is already got /***** Go through list of selected users getting the number of followed and not followed ****/ Fol_GetFollowedFromSelectedUsrs (&NumFollowed,&NumNotFollowed); /***** Show question to confirm ****/ if (NumNotFollowed) { if (NumNotFollowed == 1) Ale_ShowAlertAndButton (NextAction,NULL,NULL, Fol_PutParSelectedUsrsCods,&Gbl.Usrs.Selected, Btn_CREATE_BUTTON,Txt_Follow, Ale_QUESTION,Txt_Do_you_want_to_follow_the_selected_user_whom_you_do_not_follow_yet); else Ale_ShowAlertAndButton (NextAction,NULL,NULL, Fol_PutParSelectedUsrsCods,&Gbl.Usrs.Selected, Btn_CREATE_BUTTON,Txt_Follow, Ale_QUESTION,Txt_Do_you_want_to_follow_the_X_selected_users_whom_you_do_not_follow_yet, NumNotFollowed); } /***** Free memory used by list of selected users' codes *****/ Usr_FreeListsSelectedEncryptedUsrsCods (&Gbl.Usrs.Selected); } void Fol_ReqUnfollowStds (void) { Fol_RequestUnfollowUsrs (ActUnfSevStd); } void Fol_ReqUnfollowTchs (void) { Fol_RequestUnfollowUsrs (ActUnfSevTch); } static void Fol_RequestUnfollowUsrs (Act_Action_t NextAction) { extern const char *Txt_Do_you_want_to_stop_following_the_selected_user_whom_you_follow; extern const char *Txt_Do_you_want_to_stop_following_the_X_selected_users_whom_you_follow; extern const char *Txt_Unfollow; unsigned NumFollowed; unsigned NumNotFollowed; // List of selected users is already got /***** Go through list of selected users getting the number of followed and not followed ****/ Fol_GetFollowedFromSelectedUsrs (&NumFollowed,&NumNotFollowed); /***** Show question to confirm ****/ if (NumFollowed) { if (NumFollowed == 1) Ale_ShowAlertAndButton (NextAction,NULL,NULL, Fol_PutParSelectedUsrsCods,&Gbl.Usrs.Selected, Btn_CREATE_BUTTON,Txt_Unfollow, Ale_QUESTION,Txt_Do_you_want_to_stop_following_the_selected_user_whom_you_follow); else Ale_ShowAlertAndButton (NextAction,NULL,NULL, Fol_PutParSelectedUsrsCods,&Gbl.Usrs.Selected, Btn_CREATE_BUTTON,Txt_Unfollow, Ale_QUESTION,Txt_Do_you_want_to_stop_following_the_X_selected_users_whom_you_follow, NumFollowed); } /***** Free memory used by list of selected users' codes *****/ Usr_FreeListsSelectedEncryptedUsrsCods (&Gbl.Usrs.Selected); } static void Fol_PutParSelectedUsrsCods (void *SelectedUsrs) { if (SelectedUsrs) Usr_PutParSelectedUsrsCods ((struct Usr_SelectedUsrs *) SelectedUsrs); } /*****************************************************************************/ /**** Go through the list getting the number of followed and not followed ****/ /*****************************************************************************/ static void Fol_GetFollowedFromSelectedUsrs (unsigned *NumFollowed, unsigned *NumNotFollowed) { extern const char *Txt_Selected_users_X_Followed_Y_Not_followed_Z; struct Usr_Data UsrDat; const char *Ptr; unsigned NumUsrs = 0; /***** Initialize structure with user's data *****/ Usr_UsrDataConstructor (&UsrDat); /***** Check users to know if I follow them *****/ *NumFollowed = 0; Ptr = Gbl.Usrs.Selected.List[Rol_UNK]; while (*Ptr) { Par_GetNextStrUntilSeparParMult (&Ptr,UsrDat.EnUsrCod, Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64); Usr_GetUsrCodFromEncryptedUsrCod (&UsrDat); if (Gbl.Usrs.Me.UsrDat.UsrCod != UsrDat.UsrCod) // Skip me if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&UsrDat, Usr_DONT_GET_PREFS, Usr_DONT_GET_ROLE_IN_CRS)) // Get from the database the data of the student if (Enr_CheckIfUsrBelongsToCurrentCrs (&UsrDat) == Usr_BELONG) { /* Check if I follow this user, and update number of users */ if (Fol_DB_CheckUsrIsFollowerOf (Gbl.Usrs.Me.UsrDat.UsrCod, UsrDat.UsrCod)) // I follow user (*NumFollowed)++; NumUsrs++; } } /***** Free memory used for user's data *****/ Usr_UsrDataDestructor (&UsrDat); /***** Show alert ****/ *NumNotFollowed = NumUsrs - *NumFollowed; Ale_ShowAlert (Ale_INFO,Txt_Selected_users_X_Followed_Y_Not_followed_Z, NumUsrs,*NumFollowed,*NumNotFollowed); } /*****************************************************************************/ /********************** Follow/unfollow several users ************************/ /*****************************************************************************/ void Fol_FollowUsrs () { extern const char *Txt_You_have_followed_one_user; extern const char *Txt_You_have_followed_X_users; const char *Ptr; struct Usr_Data UsrDat; unsigned NumFollowed = 0; /***** Get list of selected users if not already got *****/ Usr_GetListsSelectedEncryptedUsrsCods (&Gbl.Usrs.Selected); /***** Initialize structure with user's data *****/ Usr_UsrDataConstructor (&UsrDat); /***** Check users to know if I follow them *****/ Ptr = Gbl.Usrs.Selected.List[Rol_UNK]; while (*Ptr) { Par_GetNextStrUntilSeparParMult (&Ptr,UsrDat.EnUsrCod, Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64); Usr_GetUsrCodFromEncryptedUsrCod (&UsrDat); if (Gbl.Usrs.Me.UsrDat.UsrCod != UsrDat.UsrCod) // Skip me if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&UsrDat, // Get user's data from database Usr_DONT_GET_PREFS, Usr_DONT_GET_ROLE_IN_CRS)) if (Enr_CheckIfUsrBelongsToCurrentCrs (&UsrDat) == Usr_BELONG) /* If I don't follow this user ==> follow him/her */ if (!Fol_DB_CheckUsrIsFollowerOf (Gbl.Usrs.Me.UsrDat.UsrCod, UsrDat.UsrCod)) { Fol_FollowUsr (&UsrDat); NumFollowed++; } } /***** Free memory used for user's data *****/ Usr_UsrDataDestructor (&UsrDat); /***** Free memory used by list of selected users' codes *****/ Usr_FreeListsSelectedEncryptedUsrsCods (&Gbl.Usrs.Selected); /***** Show alert *****/ if (NumFollowed == 1) Ale_ShowAlert (Ale_SUCCESS,Txt_You_have_followed_one_user); else Ale_ShowAlert (Ale_SUCCESS,Txt_You_have_followed_X_users, NumFollowed); } void Fol_UnfollowUsrs (void) { extern const char *Txt_You_have_stopped_following_one_user; extern const char *Txt_You_have_stopped_following_X_users; const char *Ptr; struct Usr_Data UsrDat; unsigned NumUnfollowed = 0; /***** Get list of selected users if not already got *****/ Usr_GetListsSelectedEncryptedUsrsCods (&Gbl.Usrs.Selected); /***** Initialize structure with user's data *****/ Usr_UsrDataConstructor (&UsrDat); /***** Check users to know if I follow them *****/ Ptr = Gbl.Usrs.Selected.List[Rol_UNK]; while (*Ptr) { Par_GetNextStrUntilSeparParMult (&Ptr,UsrDat.EnUsrCod, Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64); Usr_GetUsrCodFromEncryptedUsrCod (&UsrDat); if (Gbl.Usrs.Me.UsrDat.UsrCod != UsrDat.UsrCod) // Skip me if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&UsrDat, // Get user's data from database Usr_DONT_GET_PREFS, Usr_DONT_GET_ROLE_IN_CRS)) if (Enr_CheckIfUsrBelongsToCurrentCrs (&UsrDat) == Usr_BELONG) /* If I follow this user ==> unfollow him/her */ if (Fol_DB_CheckUsrIsFollowerOf (Gbl.Usrs.Me.UsrDat.UsrCod, UsrDat.UsrCod)) { Fol_UnfollowUsr (&UsrDat); NumUnfollowed++; } } /***** Free memory used for user's data *****/ Usr_UsrDataDestructor (&UsrDat); /***** Free memory used by list of selected users' codes *****/ Usr_FreeListsSelectedEncryptedUsrsCods (&Gbl.Usrs.Selected); /***** Show alert *****/ if (NumUnfollowed == 1) Ale_ShowAlert (Ale_SUCCESS,Txt_You_have_stopped_following_one_user); else Ale_ShowAlert (Ale_SUCCESS,Txt_You_have_stopped_following_X_users, NumUnfollowed); } /*****************************************************************************/ /******************************** Follow user ********************************/ /*****************************************************************************/ static void Fol_FollowUsr (const struct Usr_Data *UsrDat) { bool CreateNotif; bool NotifyByEmail; /***** Avoid wrong cases *****/ if (Gbl.Usrs.Me.UsrDat.UsrCod <= 0 || UsrDat->UsrCod <= 0 || Gbl.Usrs.Me.UsrDat.UsrCod == UsrDat->UsrCod) // Skip me return; /***** Follow user in database *****/ Fol_DB_FollowUsr (UsrDat->UsrCod); /***** Flush cache *****/ Fol_FlushCacheFollow (); /***** This follow must be notified by email? *****/ CreateNotif = (UsrDat->NtfEvents.CreateNotif & (1 << Ntf_EVENT_FOLLOWER)); NotifyByEmail = CreateNotif && (UsrDat->NtfEvents.SendEmail & (1 << Ntf_EVENT_FOLLOWER)); /***** Create notification for this followed. If this followed wants to receive notifications by email, activate the sending of a notification *****/ if (CreateNotif) Ntf_DB_StoreNotifyEventToUsr (Ntf_EVENT_FOLLOWER,UsrDat->UsrCod,Gbl.Usrs.Me.UsrDat.UsrCod, (Ntf_Status_t) (NotifyByEmail ? Ntf_STATUS_BIT_EMAIL : 0), Gbl.Hierarchy.Node[Hie_INS].HieCod, Gbl.Hierarchy.Node[Hie_CTR].HieCod, Gbl.Hierarchy.Node[Hie_DEG].HieCod, Gbl.Hierarchy.Node[Hie_CRS].HieCod); } /*****************************************************************************/ /******************************* Unfollow user *******************************/ /*****************************************************************************/ static void Fol_UnfollowUsr (const struct Usr_Data *UsrDat) { /***** Avoid wrong cases *****/ if (Gbl.Usrs.Me.UsrDat.UsrCod <= 0 || UsrDat->UsrCod <= 0 || Gbl.Usrs.Me.UsrDat.UsrCod == UsrDat->UsrCod) // Skip me return; /***** Unfollow user in database *****/ Fol_DB_UnfollowUsr (UsrDat->UsrCod); /***** Flush cache *****/ Fol_FlushCacheFollow (); } /*****************************************************************************/ /****** Get and show ranking of users attending to number of followers *******/ /*****************************************************************************/ void Fol_GetAndShowRankingFollowers (void) { MYSQL_RES *mysql_res; unsigned NumUsrs; /***** Get ranking from database *****/ NumUsrs = Fol_DB_GetRankingFollowers (&mysql_res); /***** Show ranking *****/ Prf_ShowRankingFigure (&mysql_res,NumUsrs); } /*****************************************************************************/ /********************* Get notification of a new follower ********************/ /*****************************************************************************/ void Fol_GetNotifFollower (char SummaryStr[Ntf_MAX_BYTES_SUMMARY + 1], char **ContentStr) { SummaryStr[0] = '\0'; // Return nothing on error if ((*ContentStr = malloc (1))) *ContentStr[0] = '\0'; } /*****************************************************************************/ /*********************** Remove user from user follow ************************/ /*****************************************************************************/ void Fol_RemoveUsrFromUsrFollow (long UsrCod) { /***** Remove user from followers and followed *****/ Fol_DB_RemoveUsrFromUsrFollow (UsrCod); /***** Flush cache *****/ Fol_FlushCacheFollow (); } /*****************************************************************************/ /************** Get and show number of following and followers ***************/ /*****************************************************************************/ void Fol_GetAndShowFollowStats (void) { extern const char *Hlp_ANALYTICS_Figures_followed_followers; extern const char *Txt_FIGURE_TYPES[Fig_NUM_FIGURES]; extern const char *Txt_Users; extern const char *Txt_Number_of_users; extern const char *Txt_PERCENT_of_users; extern const char *Txt_Followed; extern const char *Txt_Followers; extern const char *Txt_FollowPerFollow[2]; unsigned Fol; unsigned NumUsrsTotal; unsigned NumUsrs; double Average; /***** Begin box and table *****/ Box_BoxTableBegin (Txt_FIGURE_TYPES[Fig_FOLLOW],NULL,NULL, Hlp_ANALYTICS_Figures_followed_followers,Box_NOT_CLOSABLE,2); /***** Heading row *****/ HTM_TR_Begin (NULL); HTM_TH (Txt_Users ,HTM_HEAD_LEFT); HTM_TH (Txt_Number_of_users ,HTM_HEAD_RIGHT); HTM_TH (Txt_PERCENT_of_users,HTM_HEAD_RIGHT); HTM_TR_End (); /***** Get total number of users *****/ NumUsrsTotal = Usr_GetTotalNumberOfUsers (); /***** Get total number of following/followers from database *****/ for (Fol = 0; Fol < 2; Fol++) { NumUsrs = Fol_DB_GetNumFollowinFollowers (Fol); /***** Write number of followed / followers *****/ HTM_TR_Begin (NULL); HTM_TD_Txt_Left (Fol == 0 ? Txt_Followed : Txt_Followers); HTM_TD_Unsigned (NumUsrs); HTM_TD_Percentage (NumUsrs,NumUsrsTotal); HTM_TR_End (); } /***** Write number of followed/followers per follower/followed *****/ for (Fol = 0; Fol < 2; Fol++) { Average = Fol_DB_GetNumFollowedPerFollower (Fol); /***** Write number of followed per follower *****/ HTM_TR_Begin (NULL); HTM_TD_Txt_Left (Txt_FollowPerFollow[Fol]); HTM_TD_Double2Decimals (Average); HTM_TD_Empty (1); HTM_TR_End (); } /***** End table and box *****/ Box_BoxTableEnd (); }