// swad_message.c: messages between users
/*
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 asprintf
#include // For free
#include // For string functions
#include // For time
#include "swad_action.h"
#include "swad_box.h"
#include "swad_config.h"
#include "swad_course.h"
#include "swad_database.h"
#include "swad_form.h"
#include "swad_forum.h"
#include "swad_global.h"
#include "swad_group.h"
#include "swad_HTML.h"
#include "swad_ID.h"
#include "swad_message.h"
#include "swad_notification.h"
#include "swad_parameter.h"
#include "swad_photo.h"
#include "swad_profile.h"
#include "swad_user.h"
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/***************************** Private constants *****************************/
/*****************************************************************************/
#define Msg_MAX_BYTES_MESSAGES_QUERY (4 * 1024 - 1)
// Forum images will be saved with:
// - maximum width of Msg_IMAGE_SAVED_MAX_HEIGHT
// - maximum height of Msg_IMAGE_SAVED_MAX_HEIGHT
// - maintaining the original aspect ratio (aspect ratio recommended: 3:2)
#define Msg_IMAGE_SAVED_MAX_WIDTH 768
#define Msg_IMAGE_SAVED_MAX_HEIGHT 512
#define Msg_IMAGE_SAVED_QUALITY 75 // 1 to 100
static const Pag_WhatPaginate_t Msg_WhatPaginate[Msg_NUM_TYPES_OF_MSGS] =
{
Pag_MESSAGES_RECEIVED, // Msg_MESSAGES_RECEIVED
Pag_MESSAGES_SENT // Msg_MESSAGES_SENT
};
/*****************************************************************************/
/******************************** Private types ******************************/
/*****************************************************************************/
/*****************************************************************************/
/**************************** Internal prototypes ****************************/
/*****************************************************************************/
static void Msg_PutFormMsgUsrs (char Content[Cns_MAX_BYTES_LONG_TEXT + 1]);
static void Msg_ShowSentOrReceivedMessages (void);
static unsigned long Msg_GetNumUsrsBannedByMe (void);
static void Msg_PutLinkToViewBannedUsers(void);
static unsigned long Msg_GetSentOrReceivedMsgs (long UsrCod,
long FilterCrsCod,
const char *FilterFromToSubquery,
MYSQL_RES **mysql_res);
static char *Msg_WriteNumMsgs (unsigned NumUnreadMsgs);
static void Msg_PutIconsListMsgs (void);
static void Msg_PutHiddenParamsOneMsg (void);
static void Msg_ShowFormToShowOnlyUnreadMessages (void);
static void Msg_GetParamOnlyUnreadMsgs (void);
static void Msg_ShowASentOrReceivedMessage (long MsgNum,long MsgCod);
static void Msg_GetStatusOfSentMsg (long MsgCod,bool *Expanded);
static void Msg_GetStatusOfReceivedMsg (long MsgCod,bool *Open,bool *Replied,bool *Expanded);
static long Msg_GetParamMsgCod (void);
static void Msg_PutLinkToShowMorePotentialRecipients (void);
static void Msg_PutParamsShowMorePotentialRecipients (void);
static void Msg_PutParamsWriteMsg (void);
static void Msg_ShowOneUniqueRecipient (void);
static void Msg_WriteFormUsrsIDsOrNicksOtherRecipients (void);
static void Msg_WriteFormSubjectAndContentMsgToUsrs (char Content[Cns_MAX_BYTES_LONG_TEXT + 1]);
static void Msg_ShowNumMsgsDeleted (unsigned NumMsgs);
static void Msg_MakeFilterFromToSubquery (char FilterFromToSubquery[Msg_MAX_BYTES_MESSAGES_QUERY + 1]);
static void Msg_ExpandSentMsg (long MsgCod);
static void Msg_ExpandReceivedMsg (long MsgCod);
static void Msg_ContractSentMsg (long MsgCod);
static void Msg_ContractReceivedMsg (long MsgCod);
static long Msg_InsertNewMsg (const char *Subject,const char *Content,
struct Media *Media);
static unsigned long Msg_DelSomeRecOrSntMsgsUsr (Msg_TypeOfMessages_t TypeOfMessages,long UsrCod,
long FilterCrsCod,const char *FilterFromToSubquery);
static void Msg_InsertReceivedMsgIntoDB (long MsgCod,long UsrCod,bool NotifyByEmail);
static void Msg_SetReceivedMsgAsReplied (long MsgCod);
static void Msg_MoveReceivedMsgToDeleted (long MsgCod,long UsrCod);
static void Msg_MoveSentMsgToDeleted (long MsgCod);
static void Msg_MoveMsgContentToDeleted (long MsgCod);
static bool Msg_CheckIfSentMsgIsDeleted (long MsgCod);
static bool Msg_CheckIfReceivedMsgIsDeletedForAllItsRecipients (long MsgCod);
static unsigned Msg_GetNumUnreadMsgs (long FilterCrsCod,const char *FilterFromToSubquery);
static void Msg_GetMsgSntData (long MsgCod,long *CrsCod,long *UsrCod,
time_t *CreatTimeUTC,
char Subject[Cns_MAX_BYTES_SUBJECT + 1],
bool *Deleted);
static void Msg_GetMsgContent (long MsgCod,char Content[Cns_MAX_BYTES_LONG_TEXT + 1],
struct Media *Media);
static void Msg_WriteSentOrReceivedMsgSubject (long MsgCod,const char *Subject,bool Open,bool Expanded);
static void Msg_WriteFormToReply (long MsgCod,long CrsCod,
bool ThisCrs,bool Replied,
const struct UsrData *UsrDat);
static void Msg_WriteMsgFrom (struct UsrData *UsrDat,bool Deleted);
static void Msg_WriteMsgTo (long MsgCod);
static void Msg_PutFormToBanSender (struct UsrData *UsrDat);
static void Msg_PutFormToUnbanSender (struct UsrData *UsrDat);
static void Msg_UnbanSender (void);
/*****************************************************************************/
/***************** Put a form to write a new message to users ****************/
/*****************************************************************************/
void Msg_FormMsgUsrs (void)
{
char Content[Cns_MAX_BYTES_LONG_TEXT + 1];
/***** Get possible hidden subject and content of the message *****/
Par_GetParToHTML ("HiddenSubject",Gbl.Msg.Subject,Cns_MAX_BYTES_SUBJECT);
Par_GetParAndChangeFormat ("HiddenContent",Content,Cns_MAX_BYTES_LONG_TEXT,
Str_TO_TEXT,false);
/***** Show a form to compose a message to users *****/
Msg_PutFormMsgUsrs (Content);
}
/*****************************************************************************/
/***************** Put a form to write a new message to users ****************/
/*****************************************************************************/
static void Msg_PutFormMsgUsrs (char Content[Cns_MAX_BYTES_LONG_TEXT + 1])
{
extern const char *Hlp_MESSAGES_Write;
extern const char *The_ClassFormInBox[The_NUM_THEMES];
extern const char *Txt_Reply_message;
extern const char *Txt_New_message;
extern const char *Txt_MSG_To;
extern const char *Txt_Send_message;
unsigned NumUsrsInCrs = 0; // Initialized to avoid warning
bool ShowUsrsInCrs = false;
bool GetUsrsInCrs;
Gbl.Usrs.LstUsrs[Rol_STD].NumUsrs =
Gbl.Usrs.LstUsrs[Rol_NET].NumUsrs =
Gbl.Usrs.LstUsrs[Rol_TCH].NumUsrs = 0;
/***** Get parameter that indicates if the message is a reply to another message *****/
if ((Gbl.Msg.Reply.IsReply = Par_GetParToBool ("IsReply")))
/* Get original message code */
if ((Gbl.Msg.Reply.OriginalMsgCod = Msg_GetParamMsgCod ()) <= 0)
Lay_ShowErrorAndExit ("Wrong code of message.");
/***** Get user's code of possible preselected recipient *****/
if (Usr_GetParamOtherUsrCodEncryptedAndGetUsrData ()) // There is a preselected recipient
/* Get who to show as potential recipients:
- only the selected recipient
- any user (default) */
Gbl.Msg.ShowOnlyOneRecipient = Par_GetParToBool ("ShowOnlyOneRecipient");
else
Gbl.Msg.ShowOnlyOneRecipient = false;
GetUsrsInCrs = !Gbl.Msg.ShowOnlyOneRecipient && // Show list of potential recipients
(Gbl.Usrs.Me.IBelongToCurrentCrs || // If there is a course selected and I belong to it
Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM);
if (GetUsrsInCrs)
{
/***** Get and update type of list,
number of columns in class photo
and preference about view photos *****/
Usr_GetAndUpdatePrefsAboutUsrList ();
/***** Get groups to show ******/
Grp_GetParCodsSeveralGrpsToShowUsrs ();
/***** Get and order lists of users from this course *****/
Usr_GetListUsrs (Hie_CRS,Rol_STD);
Usr_GetListUsrs (Hie_CRS,Rol_NET);
Usr_GetListUsrs (Hie_CRS,Rol_TCH);
NumUsrsInCrs = Gbl.Usrs.LstUsrs[Rol_STD].NumUsrs + // Students
Gbl.Usrs.LstUsrs[Rol_NET].NumUsrs + // Non-editing teachers
Gbl.Usrs.LstUsrs[Rol_TCH].NumUsrs; // Teachers
}
/***** Start box *****/
Box_StartBox (NULL,Gbl.Msg.Reply.IsReply ? Txt_Reply_message :
Txt_New_message,NULL,
Hlp_MESSAGES_Write,Box_NOT_CLOSABLE);
if (Gbl.Msg.ShowOnlyOneRecipient)
/***** Form to show several potential recipients *****/
Msg_PutLinkToShowMorePotentialRecipients ();
else
{
/***** Get list of users belonging to the current course *****/
if (GetUsrsInCrs)
{
/***** Form to select groups *****/
Grp_ShowFormToSelectSeveralGroups (Msg_PutParamsWriteMsg,
Grp_MY_GROUPS);
/***** Start section with user list *****/
Lay_StartSection (Usr_USER_LIST_SECTION_ID);
if (NumUsrsInCrs)
{
/***** Form to select type of list used for select several users *****/
Usr_ShowFormsToSelectUsrListType (Msg_PutParamsWriteMsg);
/***** Put link to register students *****/
Enr_CheckStdsAndPutButtonToRegisterStdsInCurrentCrs ();
/***** Check if it's a big list *****/
ShowUsrsInCrs = Usr_GetIfShowBigList (NumUsrsInCrs,Msg_PutParamsWriteMsg,
"CopyMessageToHiddenFields();");
if (ShowUsrsInCrs)
/***** Get lists of selected users *****/
Usr_GetListsSelectedUsrsCods ();
}
/***** End section with user list *****/
Lay_EndSection ();
}
/***** Get list of users' IDs or nicknames written explicitely *****/
Usr_GetListMsgRecipientsWrittenExplicitelyBySender (false);
}
/***** Begin form to select recipients and write the message *****/
Frm_StartForm (ActRcvMsgUsr);
if (Gbl.Msg.Reply.IsReply)
{
Par_PutHiddenParamChar ("IsReply",'Y');
Msg_PutHiddenParamMsgCod (Gbl.Msg.Reply.OriginalMsgCod);
}
if (Gbl.Usrs.Other.UsrDat.UsrCod > 0)
{
Usr_PutParamOtherUsrCodEncrypted ();
if (Gbl.Msg.ShowOnlyOneRecipient)
Par_PutHiddenParamChar ("ShowOnlyOneRecipient",'Y');
}
/***** Begin table *****/
HTM_TABLE_BeginCenterPadding (2);
/***** "To:" section (recipients) *****/
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"%s RT\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
fprintf (Gbl.F.Out,"%s:",Txt_MSG_To);
HTM_TD_End ();
HTM_TD_Begin ("class=\"LT\"");
if (Gbl.Msg.ShowOnlyOneRecipient)
/***** Show only one user as recipient *****/
Msg_ShowOneUniqueRecipient ();
else
{
/***** Show potential recipients *****/
HTM_TABLE_BeginWide ();
if (ShowUsrsInCrs)
{
Usr_ListUsersToSelect (Rol_TCH); // All teachers in course
Usr_ListUsersToSelect (Rol_NET); // All non-editing teachers in course
Usr_ListUsersToSelect (Rol_STD); // All students in selected groups
}
Msg_WriteFormUsrsIDsOrNicksOtherRecipients (); // Other users (nicknames)
HTM_TABLE_End ();
}
HTM_TD_End ();
HTM_TR_End ();
/***** Subject and content sections *****/
Msg_WriteFormSubjectAndContentMsgToUsrs (Content);
/***** End table *****/
HTM_TABLE_End ();
/***** Help for text editor and send button *****/
Lay_HelpPlainEditor ();
/***** Attached image (optional) *****/
Med_PutMediaUploader (-1,"MSG_MED_INPUT");
/***** Send button *****/
Btn_PutCreateButton (Txt_Send_message);
/***** End form *****/
Frm_EndForm ();
/***** End box *****/
Box_EndBox ();
/***** Free memory used by the list of nicknames *****/
Usr_FreeListOtherRecipients ();
/***** Free memory used for by the lists of users *****/
if (GetUsrsInCrs)
{
Usr_FreeUsrsList (Rol_TCH);
Usr_FreeUsrsList (Rol_NET);
Usr_FreeUsrsList (Rol_STD);
}
/***** Free memory used by list of selected users' codes *****/
Usr_FreeListsSelectedUsrsCods ();
/***** Free memory for list of selected groups *****/
Grp_FreeListCodSelectedGrps ();
}
/*****************************************************************************/
/********** Put contextual link to show more potential recipients ************/
/*****************************************************************************/
static void Msg_PutLinkToShowMorePotentialRecipients (void)
{
extern const char *Txt_Show_more_recipients;
fprintf (Gbl.F.Out,"