// swad_timeline_comment.c: social timeline comments /* 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 PATH_MAX #include // For asprintf #include // For free #include "swad_action_list.h" #include "swad_box.h" #include "swad_error.h" #include "swad_forum.h" #include "swad_global.h" #include "swad_message.h" #include "swad_notification_database.h" #include "swad_parameter.h" #include "swad_parameter_code.h" #include "swad_photo.h" #include "swad_profile.h" #include "swad_timeline.h" #include "swad_timeline_database.h" #include "swad_timeline_form.h" #include "swad_timeline_note.h" #include "swad_timeline_publication.h" /*****************************************************************************/ /************************* Private constants and types ***********************/ /*****************************************************************************/ #define TmlCom_NUM_VISIBLE_COMMENTS 3 // Maximum number of comments visible before expanding them #define TmlCom_NUM_CONTRACT_EXPAND 2 typedef enum { TmlCom_CONTRACT = 0, TmlCom_EXPAND = 1, } TmlCom_ContractExpand_t; /*****************************************************************************/ /************** External global variables from others modules ****************/ /*****************************************************************************/ extern struct Globals Gbl; /*****************************************************************************/ /***************************** Private prototypes ****************************/ /*****************************************************************************/ static void TmlCom_ShowAuthorPhoto (struct Usr_Data *UsrDat); static void TmlCom_PutFormToWriteNewComm (const struct Tml_Timeline *Timeline, long NotCod); static unsigned TmlCom_WriteHiddenComms (struct Tml_Timeline *Timeline, long NotCod, char IdComms[Frm_MAX_BYTES_ID + 1], unsigned NumInitialCommsToGet); static void TmlCom_ListComms (const struct Tml_Timeline *Timeline, unsigned NumComms,MYSQL_RES *mysql_res); static void TmlCom_WriteOneCommInList (const struct Tml_Timeline *Timeline, MYSQL_RES *mysql_res); static void TmlCom_LinkToShowComms (TmlCom_ContractExpand_t ConExp, const char IdComms[Frm_MAX_BYTES_ID + 1], unsigned NumComms); static void TmlCom_PutIconToToggleComms (const char *UniqueId, const char *Icon,const char *Text); static void TmlCom_CheckAndWriteComm (const struct Tml_Timeline *Timeline, struct TmlCom_Comment *Com); static void TmlCom_WriteComm (const struct Tml_Timeline *Timeline, struct TmlCom_Comment *Com); static void TmlCom_WriteAuthorTimeAndContent (struct TmlCom_Comment *Com, const struct Usr_Data *UsrDat); static void TmlCom_WriteAuthorName (const struct Usr_Data *UsrDat); static void TmlCom_WriteContent (struct TmlCom_Comment *Com); static void TmlCom_WriteButtons (const struct Tml_Timeline *Timeline, const struct TmlCom_Comment *Com, const struct Usr_Data *UsrDat); static void TmlCom_PutFormToRemoveComm (const struct Tml_Timeline *Timeline, long PubCod); static long TmlCom_ReceiveComm (void); static void TmlCom_ReqRemComm (struct Tml_Timeline *Timeline); static void TmlCom_PutParsRemoveComm (void *Timeline); static void TmlCom_RemoveComm (void); static void TmlCom_GetCommDataFromRow (MYSQL_RES *mysql_res, struct TmlCom_Comment *Com); static void TmlCom_ResetComm (struct TmlCom_Comment *Com); /*****************************************************************************/ /********* Put an icon to toggle on/off the form to comment a note ***********/ /*****************************************************************************/ void TmlCom_PutIconToToggleComm (const struct Tml_Timeline *Timeline, const char UniqueId[Frm_MAX_BYTES_ID + 1]) { /***** Begin container *****/ HTM_DIV_Begin ("id=\"%s_ico\" class=\"Tml_ICO_COM_OFF\"",UniqueId); /* Begin anchor */ HTM_A_Begin ("href=\"\" onclick=\"toggleNewComment ('%s');" "return false;\"", UniqueId); /* Icon to toggle comment */ Ico_PutIcon ("comment-regular.svg",Ico_BLACK, Act_GetActionText (Timeline->UsrOrGbl == TmlUsr_TIMELINE_GBL ? ActRcvComGblTL : ActRcvComUsrTL), "CONTEXT_ICO16x16"); /* End anchor */ HTM_A_End (); /***** End container *****/ HTM_DIV_End (); } /*****************************************************************************/ /********** Put an icon to toggle on/off the form to comment a note **********/ /*****************************************************************************/ void TmlCom_PutIconCommDisabled (void) { extern const char *Txt_not_available; /***** Disabled icon to comment a note *****/ /* Begin container */ HTM_DIV_Begin ("class=\"Tml_ICO_COM_OFF Tml_ICO_DISABLED\""); /* Disabled icon */ Ico_PutIcon ("comment-regular.svg",Ico_BLACK,Txt_not_available,"ICO16x16"); /* End container */ HTM_DIV_End (); } /*****************************************************************************/ /************************* Form to comment a note ****************************/ /*****************************************************************************/ void TmlCom_PutPhotoAndFormToWriteNewComm (const struct Tml_Timeline *Timeline, long NotCod, const char IdNewComm[Frm_MAX_BYTES_ID + 1]) { /***** Begin container *****/ HTM_DIV_Begin ("id=\"%s\" class=\"Tml_FORM_NEW_COM Tml_RIGHT_WIDTH\"" " style=\"display:none;\"", IdNewComm); /***** Left: write author's photo (my photo) *****/ TmlCom_ShowAuthorPhoto (&Gbl.Usrs.Me.UsrDat); /***** Right: form to write the comment *****/ TmlCom_PutFormToWriteNewComm (Timeline,NotCod); /***** End container *****/ HTM_DIV_End (); } /*****************************************************************************/ /********************* Show photo of author of a comment *********************/ /*****************************************************************************/ static void TmlCom_ShowAuthorPhoto (struct Usr_Data *UsrDat) { static const char *ClassPhoto[PhoSha_NUM_SHAPES] = { [PhoSha_SHAPE_CIRCLE ] = "PHOTOC30x40", [PhoSha_SHAPE_ELLIPSE ] = "PHOTOE30x40", [PhoSha_SHAPE_OVAL ] = "PHOTOO30x40", [PhoSha_SHAPE_RECTANGLE] = "PHOTOR30x40", }; /***** Show author's photo *****/ /* Begin container */ HTM_DIV_Begin ("class=\"Tml_COM_PHOTO\""); /* Author's photo */ Pho_ShowUsrPhotoIfAllowed (UsrDat, ClassPhoto[Gbl.Prefs.PhotoShape],Pho_ZOOM); /* End container */ HTM_DIV_End (); } /*****************************************************************************/ /**************************** Form with textarea *****************************/ /*****************************************************************************/ static void TmlCom_PutFormToWriteNewComm (const struct Tml_Timeline *Timeline, long NotCod) { extern const char *Txt_New_TIMELINE_comment; char *ClassTextArea; /***** Begin container *****/ HTM_DIV_Begin ("class=\"Tml_COM_CONT Tml_COM_WIDTH\""); /***** Begin form to write the post *****/ TmlFrm_BeginForm (Timeline,TmlFrm_RECEIVE_COMM); ParCod_PutPar (ParCod_Not,NotCod); /***** Textarea and button *****/ if (asprintf (&ClassTextArea,"Tml_COM_TEXTAREA Tml_COM_WIDTH INPUT_%s", The_GetSuffix ()) < 0) Err_NotEnoughMemoryExit (); TmlPst_PutTextarea (Txt_New_TIMELINE_comment,ClassTextArea); free (ClassTextArea); /***** End form *****/ TmlFrm_EndForm (); /***** End container *****/ HTM_DIV_End (); } /*****************************************************************************/ /*********************** Write comments in a note ****************************/ /*****************************************************************************/ void TmlCom_WriteCommsInNote (const struct Tml_Timeline *Timeline, const struct TmlNot_Note *Not) { MYSQL_RES *mysql_res; unsigned NumComms; unsigned NumInitialComms; unsigned NumFinalCommsToGet; unsigned NumFinalComms; char IdComms[Frm_MAX_BYTES_ID + 1]; /***** Get number of comments in note *****/ NumComms = Tml_DB_GetNumCommsInNote (Not->NotCod); /***** Trivial check: if no comments ==> nothing to do *****/ if (!NumComms) return; /***** Compute how many initial comments will be hidden and how many final comments will be visible *****/ // Never hide only one comment // So, the number of comments initially hidden must be 0 or >= 2 if (NumComms <= TmlCom_NUM_VISIBLE_COMMENTS + 1) { NumInitialComms = 0; NumFinalCommsToGet = NumComms; } else { NumInitialComms = NumComms - TmlCom_NUM_VISIBLE_COMMENTS; NumFinalCommsToGet = TmlCom_NUM_VISIBLE_COMMENTS; } /***** Get final comments of this note from database *****/ NumFinalComms = Tml_DB_GetFinalComms (Not->NotCod,NumFinalCommsToGet,&mysql_res); /* Before clicking "See prev..." --> After clicking "See prev..." _________________________________ _________________________________ | div con_ | | div con_ | | (hidden) | | (visible) | | _____________________________ | | _____________________________ | | | v See only the latest | | | | v See only the latest | | | |_________(contract)__________| | | |_________(contract)__________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | div | | div updated | | which content | | _____________________________ | | will be updated via AJAX | | | ul com_ | | | (parent of parent of form) | | | _________________________ | | | | | | | li (comment 1) | | | | | | | |_________________________| | | | | | | | ... | | | | | | | |_________________________| | | | | | | | li (comment n) | | | | | --> | | |_________________________| | | | | | |_____________________________| | | _____________________________ | | _____________________________ | | | div exp_ | | | | div exp_ | | | | (visible) | | | | (hidden) | | | | _________________________ | | | | | | | | | form | | | | | | | | | | _____________________ | | | | | _____________________ | | | | | | ^ See prev.comments | | | | | | | ^ See prev.comments | | | | | | |_______(expand)______| | | | | | |_______(expand)______| | | | | |_________________________| | | | | | | | |_____________________________| | | |_____________________________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | ul | | ul | | _________________________ | | _________________________ | | | li (comment n+1) | | | | li (comment n+1) | | | |_________________________| | | |_________________________| | | | ... | | | | ... | | | |_________________________| | | |_________________________| | | | li (comment m) | | | | li (comment m) | | | |_________________________| | | |_________________________| | |_________________________________| |_________________________________| */ /***** Link to show initial hidden comments *****/ if (NumInitialComms) { /***** Create unique id for list of hidden comments *****/ Frm_SetUniqueId (IdComms); /***** Link (initially hidden) to show only the latest comments *****/ TmlCom_LinkToShowComms (TmlCom_CONTRACT,IdComms,NumFinalComms); /***** Div with form to show hidden coments, which content will be updated via AJAX *****/ TmlFrm_FormToShowHiddenComms (Not->NotCod,IdComms,NumInitialComms); } /***** List final visible comments *****/ if (NumFinalComms) { HTM_UL_Begin ("class=\"Tml_LIST\""); // Never hidden, always visible TmlCom_ListComms (Timeline,NumFinalComms,mysql_res); HTM_UL_End (); } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); } /*****************************************************************************/ /********************** Write hidden comments via AJAX ***********************/ /*****************************************************************************/ void TmlCom_ShowHiddenCommsUsr (void) { /***** Get user whom profile is displayed *****/ Usr_GetParOtherUsrCodEncryptedAndGetUsrData (); /***** Show hidden comments *****/ TmlCom_ShowHiddenCommsGbl (); } void TmlCom_ShowHiddenCommsGbl (void) { struct Tml_Timeline Timeline; long NotCod; char IdComms[Frm_MAX_BYTES_ID + 1]; unsigned NumInitialCommsToGet; unsigned NumInitialCommsGot; /***** Reset timeline context *****/ Tml_ResetTimeline (&Timeline); /***** Get parameters *****/ /* Get note code, identifier and number of comments to get */ NotCod = ParCod_GetPar (ParCod_Not); Par_GetParText ("IdComments",IdComms,Frm_MAX_BYTES_ID); NumInitialCommsToGet = (unsigned) Par_GetParLong ("NumHidCom"); /***** Write HTML inside DIV with hidden comments *****/ NumInitialCommsGot = TmlCom_WriteHiddenComms (&Timeline,NotCod,IdComms,NumInitialCommsToGet); /***** Link to show the first comments *****/ TmlCom_LinkToShowComms (TmlCom_EXPAND,IdComms,NumInitialCommsGot); } /*****************************************************************************/ /**************************** Write hidden comments **************************/ /*****************************************************************************/ // Returns the number of comments got static unsigned TmlCom_WriteHiddenComms (struct Tml_Timeline *Timeline, long NotCod, char IdComms[Frm_MAX_BYTES_ID + 1], unsigned NumInitialCommsToGet) { MYSQL_RES *mysql_res; unsigned long NumInitialCommsGot; /***** Get comments of this note from database *****/ NumInitialCommsGot = Tml_DB_GetInitialComms (NotCod, NumInitialCommsToGet, &mysql_res); /***** List comments *****/ HTM_UL_Begin ("id=\"com_%s\" class=\"Tml_LIST\"",IdComms); TmlCom_ListComms (Timeline,NumInitialCommsGot,mysql_res); HTM_UL_End (); /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return NumInitialCommsGot; } /*****************************************************************************/ /*********************** Write comments in a note ****************************/ /*****************************************************************************/ static void TmlCom_ListComms (const struct Tml_Timeline *Timeline, unsigned NumComms,MYSQL_RES *mysql_res) { unsigned NumCom; /***** List comments one by one *****/ for (NumCom = 0; NumCom < NumComms; NumCom++) TmlCom_WriteOneCommInList (Timeline,mysql_res); } /*****************************************************************************/ /************************* Write a comment in list ***************************/ /*****************************************************************************/ static void TmlCom_WriteOneCommInList (const struct Tml_Timeline *Timeline, MYSQL_RES *mysql_res) { struct TmlCom_Comment Com; /***** Initialize image *****/ Med_MediaConstructor (&Com.Content.Media); /***** Get data of comment *****/ TmlCom_GetCommDataFromRow (mysql_res,&Com); /***** Write comment *****/ HTM_LI_Begin ("class=\"Tml_COM\""); TmlCom_CheckAndWriteComm (Timeline,&Com); HTM_LI_End (); /***** Free image *****/ Med_MediaDestructor (&Com.Content.Media); } /*****************************************************************************/ /**** Link to "show only the latest comments" / "show the first comments" ****/ /*****************************************************************************/ static void TmlCom_LinkToShowComms (TmlCom_ContractExpand_t ConExp, const char IdComms[Frm_MAX_BYTES_ID + 1], unsigned NumComms) { extern const char *Txt_See_only_the_latest_X_COMMENTS; extern const char *Txt_See_the_previous_X_COMMENTS; static const struct { const char *Id; const char *Icon; const char **Text; } Link[TmlCom_NUM_CONTRACT_EXPAND] = { [TmlCom_CONTRACT] = { .Id = "con", .Icon = "angle-down.svg", .Text = &Txt_See_only_the_latest_X_COMMENTS, }, [TmlCom_EXPAND] = { .Id = "exp", .Icon = "angle-up.svg", .Text = &Txt_See_the_previous_X_COMMENTS, }, }; char *Text; /***** Link (icon and text) to show comments ****/ /* Begin container */ HTM_DIV_Begin ("id=\"%s_%s\" class=\"Tml_EXPAND_COM Tml_RIGHT_WIDTH\"" " style=\"display:none;\"", // Hidden Link[ConExp].Id,IdComms); /* Icon and text */ if (asprintf (&Text,*Link[ConExp].Text,NumComms) < 0) Err_NotEnoughMemoryExit (); TmlCom_PutIconToToggleComms (IdComms,Link[ConExp].Icon,Text); free (Text); /* End container */ HTM_DIV_End (); } /*****************************************************************************/ /********** Put an icon to toggle on/off comments in a publication ***********/ /*****************************************************************************/ static void TmlCom_PutIconToToggleComms (const char *UniqueId, const char *Icon,const char *Text) { /***** Link to toggle on/off some divs *****/ HTM_BUTTON_Begin (Text, "class=\"BT_LINK FORM_IN_%s\"" " onclick=\"toggleComments('%s');return false;\"", The_GetSuffix (),UniqueId); Ico_PutIconTextLink (Icon,Ico_BLACK,Text); HTM_BUTTON_End (); } /*****************************************************************************/ /************************** Check and write comment **************************/ /*****************************************************************************/ static void TmlCom_CheckAndWriteComm (const struct Tml_Timeline *Timeline, struct TmlCom_Comment *Com) { /* ___________________________________________ | _____ | | | \ \ || | | Author's name | Date-time | | | ||Auth.| |______________________|___________| | | ||photo| | | | author's | ||_____| | | > name, time | | | Comment | | and content > comment | | content | | | | | | | | | |__________________________________| / | | | | | \ | | | Favs |Remove| > buttons | |________|___________________________|______| / / */ /***** Trivial check: codes *****/ if (Com->PubCod <= 0 || Com->NotCod <= 0 || Com->UsrCod <= 0) { Ale_ShowAlert (Ale_ERROR,"Error in comment."); return; } /***** Write comment *****/ TmlCom_WriteComm (Timeline,Com); } /*****************************************************************************/ /******************************** Write comment ******************************/ /*****************************************************************************/ static void TmlCom_WriteComm (const struct Tml_Timeline *Timeline, struct TmlCom_Comment *Com) { struct Usr_Data UsrDat; // Author of the comment /***** Get author's data *****/ Usr_UsrDataConstructor (&UsrDat); UsrDat.UsrCod = Com->UsrCod; Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&UsrDat, Usr_DONT_GET_PREFS, Usr_DONT_GET_ROLE_IN_CRS); /***** Left: author's photo *****/ TmlCom_ShowAuthorPhoto (&UsrDat); /***** Right: author's name, time, content, and buttons *****/ /* Begin container */ HTM_DIV_Begin ("class=\"Tml_COM_CONT Tml_COM_WIDTH\""); /* Right top: author's name, time, and content */ TmlCom_WriteAuthorTimeAndContent (Com,&UsrDat); /* Right bottom: buttons */ TmlCom_WriteButtons (Timeline,Com,&UsrDat); /* End container */ HTM_DIV_End (); /***** Free memory used for user's data *****/ Usr_UsrDataDestructor (&UsrDat); } /*****************************************************************************/ /**** Write top right part of a note: author's name, time and note content ***/ /*****************************************************************************/ static void TmlCom_WriteAuthorTimeAndContent (struct TmlCom_Comment *Com, const struct Usr_Data *UsrDat) // Author { /***** Write author's full name and nickname *****/ TmlCom_WriteAuthorName (UsrDat); /***** Write date and time *****/ Tml_WriteDateTime (Com->DateTimeUTC); /***** Write content of the comment *****/ TmlCom_WriteContent (Com); } /*****************************************************************************/ /*************** Write name of author of a comment to a note *****************/ /*****************************************************************************/ static void TmlCom_WriteAuthorName (const struct Usr_Data *UsrDat) // Author { extern const char *Txt_My_public_profile; extern const char *Txt_Another_user_s_profile; const char *Title[Usr_NUM_ME_OR_OTHER] = { [Usr_ME ] = Txt_My_public_profile, [Usr_OTHER] = Txt_Another_user_s_profile, }; /***** Show user's name inside form to go to user's public profile *****/ /* Begin form */ Frm_BeginForm (ActSeeOthPubPrf); Usr_PutParUsrCodEncrypted (UsrDat->EnUsrCod); /* Author's name */ HTM_BUTTON_Submit_Begin (Title[Usr_ItsMe (UsrDat->UsrCod)], "class=\"Tml_COM_AUTHOR Tml_COM_AUTHOR_WIDTH BT_LINK DAT_%s BOLD\"", The_GetSuffix ()); HTM_Txt (UsrDat->FullName); HTM_BUTTON_End (); /* End form */ Frm_EndForm (); } /*****************************************************************************/ /************************* Write content of comment **************************/ /*****************************************************************************/ static void TmlCom_WriteContent (struct TmlCom_Comment *Com) { /***** Write content of the comment *****/ if (Com->Content.Txt[0]) { HTM_DIV_Begin ("class=\"Tml_TXT Tml_TXT_%s\"", The_GetSuffix ()); Msg_WriteMsgContent (Com->Content.Txt,true,false); HTM_DIV_End (); } /***** Show image *****/ Med_ShowMedia (&Com->Content.Media,"Tml_COM_MED_CONT Tml_COM_WIDTH", "Tml_COM_MED Tml_COM_WIDTH"); } /*****************************************************************************/ /********************* Write bottom part of a comment ************************/ /*****************************************************************************/ static void TmlCom_WriteButtons (const struct Tml_Timeline *Timeline, const struct TmlCom_Comment *Com, const struct Usr_Data *UsrDat) // Author { static unsigned NumDiv = 0; // Used to create unique div id for fav NumDiv++; /***** Begin buttons container *****/ HTM_DIV_Begin ("class=\"Tml_FOOT Tml_COM_WIDTH\""); /***** Foot column 1: fav zone *****/ HTM_DIV_Begin ("id=\"fav_com_%s_%u\" class=\"Tml_FAV_COM Tml_FAV_WIDTH\"", Cry_GetUniqueNameEncrypted (),NumDiv); TmlUsr_PutIconFavSha (TmlUsr_FAV_UNF_COMM, Com->PubCod,Com->UsrCod,Com->NumFavs, TmlUsr_SHOW_FEW_USRS); HTM_DIV_End (); /***** Foot column 2: icon to remove this comment *****/ HTM_DIV_Begin ("class=\"Tml_REM\""); if (Usr_ItsMe (UsrDat->UsrCod) == Usr_ME) // I am the author TmlCom_PutFormToRemoveComm (Timeline,Com->PubCod); HTM_DIV_End (); /***** End buttons container *****/ HTM_DIV_End (); } /*****************************************************************************/ /************************* Form to remove comment ****************************/ /*****************************************************************************/ static void TmlCom_PutFormToRemoveComm (const struct Tml_Timeline *Timeline, long PubCod) { extern Act_Action_t TmlFrm_ActionUsr[TmlFrm_NUM_ACTIONS]; extern Act_Action_t TmlFrm_ActionGbl[TmlFrm_NUM_ACTIONS]; /***** Form to remove publication *****/ /* Begin form */ TmlFrm_BeginForm (Timeline,TmlFrm_REQ_REM_COMM); ParCod_PutPar (ParCod_Pub,PubCod); /* Icon to remove */ Ico_PutIconLink ("trash.svg",Ico_RED, Gbl.Usrs.Other.UsrDat.UsrCod > 0 ? TmlFrm_ActionUsr[TmlFrm_REQ_REM_COMM] : TmlFrm_ActionGbl[TmlFrm_REQ_REM_COMM]); /* End form */ TmlFrm_EndForm (); } /*****************************************************************************/ /******************************* Comment a note ******************************/ /*****************************************************************************/ void TmlCom_ReceiveCommUsr (void) { struct Tml_Timeline Timeline; long NotCod; /***** Reset timeline context *****/ Tml_ResetTimeline (&Timeline); /***** Get user whom profile is displayed *****/ Usr_GetParOtherUsrCodEncryptedAndGetUsrData (); /***** Show user's profile *****/ Prf_ShowUsrProfile (&Gbl.Usrs.Other.UsrDat); /***** Begin section *****/ HTM_SECTION_Begin (Tml_TIMELINE_SECTION_ID); /***** Receive comment in a note and write updated timeline after commenting (user) *****/ NotCod = TmlCom_ReceiveComm (); Tml_ShowTimelineUsrHighlighting (&Timeline,NotCod); /***** End section *****/ HTM_SECTION_End (); } void TmlCom_ReceiveCommGbl (void) { struct Tml_Timeline Timeline; long NotCod; /***** Initialize timeline *****/ Tml_InitTimelineGbl (&Timeline); /***** Receive comment in a note *****/ NotCod = TmlCom_ReceiveComm (); /***** Write updated timeline after commenting (global) *****/ Tml_ShowTimelineGblHighlighting (&Timeline,NotCod); } static long TmlCom_ReceiveComm (void) { extern const char *Txt_The_post_no_longer_exists; struct TmlPst_Content Content; struct TmlNot_Note Not; struct TmlPub_Publication Pub; /***** Get data of note *****/ Not.NotCod = ParCod_GetAndCheckPar (ParCod_Not); TmlNot_GetNoteDataByCod (&Not); /***** Trivial check: note code *****/ if (Not.NotCod <= 0) { Ale_ShowAlert (Ale_WARNING,Txt_The_post_no_longer_exists); return -1L; } /***** Get the content of the comment *****/ Par_GetParAndChangeFormat ("Txt",Content.Txt,Cns_MAX_BYTES_LONG_TEXT, Str_TO_RIGOROUS_HTML,Str_REMOVE_SPACES); /***** Initialize image *****/ Med_MediaConstructor (&Content.Media); /***** Get attached image (action, file and title) *****/ Content.Media.Width = Tml_IMAGE_SAVED_MAX_WIDTH; Content.Media.Height = Tml_IMAGE_SAVED_MAX_HEIGHT; Content.Media.Quality = Tml_IMAGE_SAVED_QUALITY; Med_GetMediaFromForm (-1L,-1L,-1,&Content.Media,NULL,NULL); Ale_ShowAlerts (NULL); if (Content.Txt[0] || // Text not empty Content.Media.Status == Med_PROCESSED) // A media is attached { /***** Store media in filesystem and database *****/ Med_RemoveKeepOrStoreMedia (-1L,&Content.Media); /***** Publish *****/ /* Insert into publications */ Pub.NotCod = Not.NotCod; Pub.PublisherCod = Gbl.Usrs.Me.UsrDat.UsrCod; Pub.Type = TmlPub_COMMENT_TO_NOTE; TmlPub_PublishPubInTimeline (&Pub); // Set Pub.PubCod /* Insert comment content in the database */ Tml_DB_InsertCommContent (Pub.PubCod,&Content); /***** Store notifications about the new comment *****/ Ntf_StoreNotifyEventsToAllUsrs (Ntf_EVENT_TML_COMMENT,Pub.PubCod); /***** Analyze content and store notifications about mentions *****/ Str_AnalyzeTxtAndStoreNotifyEventToMentionedUsrs (Pub.PubCod,Content.Txt); } /***** Free image *****/ Med_MediaDestructor (&Content.Media); return Not.NotCod; } /*****************************************************************************/ /**************** Request the removal of a comment in a note *****************/ /*****************************************************************************/ void TmlCom_ReqRemComUsr (void) { struct Tml_Timeline Timeline; /***** Reset timeline context *****/ Tml_ResetTimeline (&Timeline); /***** Get user whom profile is displayed *****/ Usr_GetParOtherUsrCodEncryptedAndGetUsrData (); /***** Show user's profile *****/ Prf_ShowUsrProfile (&Gbl.Usrs.Other.UsrDat); /***** Begin section *****/ HTM_SECTION_Begin (Tml_TIMELINE_SECTION_ID); /***** Request the removal of comment in note *****/ TmlCom_ReqRemComm (&Timeline); /***** Write timeline again (user) *****/ Tml_ShowTimelineUsr (&Timeline); /***** End section *****/ HTM_SECTION_End (); } void TmlCom_ReqRemComGbl (void) { struct Tml_Timeline Timeline; /***** Initialize timeline *****/ Tml_InitTimelineGbl (&Timeline); /***** Request the removal of comment in note *****/ TmlCom_ReqRemComm (&Timeline); /***** Write timeline again (global) *****/ Tml_ShowNoteAndTimelineGbl (&Timeline); } static void TmlCom_ReqRemComm (struct Tml_Timeline *Timeline) { extern const char *Txt_Do_you_really_want_to_remove_the_following_comment; struct TmlCom_Comment Com; /***** Initialize media *****/ Med_MediaConstructor (&Com.Content.Media); /***** Get data of comment *****/ Com.PubCod = ParCod_GetAndCheckPar (ParCod_Pub); TmlCom_GetCommDataByCod (&Com); /***** Do some checks *****/ if (TmlUsr_CheckIfICanRemove (Com.PubCod,Com.UsrCod) == Usr_CAN_NOT) { Med_MediaDestructor (&Com.Content.Media); return; } /***** Show question and button to remove comment *****/ /* Begin alert */ TmlFrm_BeginAlertRemove (Txt_Do_you_really_want_to_remove_the_following_comment); /* Begin box for the comment */ Box_BoxBegin (NULL,NULL,NULL,NULL,Box_NOT_CLOSABLE); /* Indent the comment */ HTM_DIV_Begin ("class=\"Tml_LEFT_PHOTO\""); HTM_DIV_End (); /* Show the comment */ HTM_DIV_Begin ("class=\"Tml_RIGHT_CONT Tml_RIGHT_WIDTH\""); TmlCom_CheckAndWriteComm (Timeline,&Com); HTM_DIV_End (); /* End box */ Box_BoxEnd (); /* End alert */ Timeline->PubCod = Com.PubCod; // Publication to be removed TmlFrm_EndAlertRemove (Timeline,TmlFrm_REM_COMM,TmlCom_PutParsRemoveComm); /***** Free media *****/ Med_MediaDestructor (&Com.Content.Media); } /*****************************************************************************/ /******************** Put parameters to remove a comment *********************/ /*****************************************************************************/ static void TmlCom_PutParsRemoveComm (void *Timeline) { if (Timeline) { if (Gbl.Usrs.Other.UsrDat.UsrCod > 0) // User's timeline Usr_PutParOtherUsrCodEncrypted (Gbl.Usrs.Other.UsrDat.EnUsrCod); else // Global timeline Usr_PutParWho (((struct Tml_Timeline *) Timeline)->Who); ParCod_PutPar (ParCod_Pub,((struct Tml_Timeline *) Timeline)->PubCod); } } /*****************************************************************************/ /***************************** Remove a comment ******************************/ /*****************************************************************************/ void TmlCom_RemoveComUsr (void) { struct Tml_Timeline Timeline; /***** Reset timeline context *****/ Tml_ResetTimeline (&Timeline); /***** Get user whom profile is displayed *****/ Usr_GetParOtherUsrCodEncryptedAndGetUsrData (); /***** Show user's profile *****/ Prf_ShowUsrProfile (&Gbl.Usrs.Other.UsrDat); /***** Begin section *****/ HTM_SECTION_Begin (Tml_TIMELINE_SECTION_ID); /***** Remove a comment *****/ TmlCom_RemoveComm (); /***** Write updated timeline after removing (user) *****/ Tml_ShowTimelineUsr (&Timeline); /***** End section *****/ HTM_SECTION_End (); } void TmlCom_RemoveComGbl (void) { struct Tml_Timeline Timeline; /***** Initialize timeline *****/ Tml_InitTimelineGbl (&Timeline); /***** Remove a comment *****/ TmlCom_RemoveComm (); /***** Write updated timeline after removing (global) *****/ Tml_ShowNoteAndTimelineGbl (&Timeline); } static void TmlCom_RemoveComm (void) { extern const char *Txt_The_post_no_longer_exists; extern const char *Txt_Comment_removed; struct TmlCom_Comment Com; /***** Initialize media *****/ Med_MediaConstructor (&Com.Content.Media); /***** Get data of comment *****/ Com.PubCod = ParCod_GetAndCheckPar (ParCod_Pub); TmlCom_GetCommDataByCod (&Com); /***** Trivial check 1: publication code *****/ if (Com.PubCod <= 0) { Med_MediaDestructor (&Com.Content.Media); Ale_ShowAlert (Ale_WARNING,Txt_The_post_no_longer_exists); return; } /***** Trivial check 2: only if I am the author of this comment? *****/ if (Usr_ItsMe (Com.UsrCod) == Usr_OTHER) { Med_MediaDestructor (&Com.Content.Media); Err_NoPermission (); return; } /***** Remove media associated to comment and delete comment from database *****/ TmlCom_RemoveCommMediaAndDBEntries (Com.PubCod); /***** Free media *****/ Med_MediaDestructor (&Com.Content.Media); /***** Reset fields of comment *****/ TmlCom_ResetComm (&Com); /***** Message of success *****/ Ale_ShowAlert (Ale_SUCCESS,Txt_Comment_removed); } /*****************************************************************************/ /*************** Remove comment media and database entries *******************/ /*****************************************************************************/ void TmlCom_RemoveCommMediaAndDBEntries (long PubCod) { /***** Remove media associated to comment *****/ Med_RemoveMedia (Tml_DB_GetMedCodFromComm (PubCod)); /***** Mark possible notifications on this comment as removed *****/ Ntf_DB_MarkNotifAsRemoved (Ntf_EVENT_TML_COMMENT,PubCod); Ntf_DB_MarkNotifAsRemoved (Ntf_EVENT_TML_FAV ,PubCod); Ntf_DB_MarkNotifAsRemoved (Ntf_EVENT_TML_MENTION,PubCod); /***** Remove favs for this comment *****/ Tml_DB_RemoveCommFavs (PubCod); /***** Remove content of this comment *****/ Tml_DB_RemoveCommContent (PubCod); /***** Remove this comment publication *****/ Tml_DB_RemoveCommPub (PubCod); } /*****************************************************************************/ /******************* Get data of comment using its code **********************/ /*****************************************************************************/ void TmlCom_GetCommDataByCod (struct TmlCom_Comment *Com) { MYSQL_RES *mysql_res; /***** Trivial check: publication code should be > 0 *****/ if (Com->PubCod <= 0) { /***** Reset fields of comment *****/ TmlCom_ResetComm (Com); return; } /***** Get data of comment from database *****/ if (Tml_DB_GetCommDataByCod (Com->PubCod,&mysql_res)) TmlCom_GetCommDataFromRow (mysql_res,Com); else /* Reset fields of comment */ TmlCom_ResetComm (Com); /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); } /*****************************************************************************/ /********************** Get data of comment from row *************************/ /*****************************************************************************/ static void TmlCom_GetCommDataFromRow (MYSQL_RES *mysql_res, struct TmlCom_Comment *Com) { MYSQL_ROW row; /***** Get next row from result *****/ row = mysql_fetch_row (mysql_res); /* row[0]: PubCod row[1]: PublisherCod] row[2]: NotCod row[3]: TimePublish row[4]: Txt row[5]: MedCod */ /***** Get code of comment (row[0]), publisher code (row[1]) and note code (row[2) *****/ Com->PubCod = Str_ConvertStrCodToLongCod (row[0]); Com->UsrCod = Str_ConvertStrCodToLongCod (row[1]); Com->NotCod = Str_ConvertStrCodToLongCod (row[2]); /***** Get date-time of the note (row[3]) *****/ Com->DateTimeUTC = Dat_GetUNIXTimeFromStr (row[3]); /***** Get text content (row[4]) and media content (row[5]) *****/ Str_Copy (Com->Content.Txt,row[4],sizeof (Com->Content.Txt) - 1); Com->Content.Media.MedCod = Str_ConvertStrCodToLongCod (row[5]); Med_GetMediaDataByCod (&Com->Content.Media); /***** Get number of times this comment has been favourited *****/ Com->NumFavs = Tml_DB_GetNumFavers (TmlUsr_FAV_UNF_COMM, Com->PubCod,Com->UsrCod); } /*****************************************************************************/ /************************** Reset fields of comment **************************/ /*****************************************************************************/ static void TmlCom_ResetComm (struct TmlCom_Comment *Com) { Com->PubCod = Com->UsrCod = Com->NotCod = -1L; Com->DateTimeUTC = (time_t) 0; Com->Content.Txt[0] = '\0'; Com->NumFavs = 0; }