// For time_t
#include "swad_announcement.h"
#include "swad_box.h"
#include "swad_constant.h"
#include "swad_database.h"
#include "swad_exam.h"
#include "swad_follow.h"
#include "swad_form.h"
#include "swad_global.h"
#include "swad_HTML.h"
#include "swad_layout.h"
#include "swad_media.h"
#include "swad_notice.h"
#include "swad_notification.h"
#include "swad_parameter.h"
#include "swad_profile.h"
#include "swad_setting.h"
#include "swad_timeline.h"
/*****************************************************************************/
/****************************** Public constants *****************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Private constants *****************************/
/*****************************************************************************/
#define TL_NUM_VISIBLE_COMMENTS 3 // Maximum number of comments visible before expanding them
#define TL_DEF_USRS_SHOWN 5 // Default maximum number of users shown who have share/fav a note
#define TL_MAX_USRS_SHOWN 1000 // Top maximum number of users shown who have share/fav a note
#define TL_MAX_CHARS_IN_POST 1000
#define TL_ICON_ELLIPSIS "ellipsis-h.svg"
#define TL_ICON_FAV "heart.svg"
#define TL_ICON_FAVED "heart-red.svg"
#define TL_ICON_SHARE "share-alt.svg"
#define TL_ICON_SHARED "share-alt-green.svg"
typedef enum
{
TL_TIMELINE_USR, // Show the timeline of a user
TL_TIMELINE_GBL, // Show the timeline of the users follwed by me
} TL_TimelineUsrOrGbl_t;
typedef enum
{
TL_GET_ONLY_NEW_PUBS, // New publications are retrieved via AJAX
// automatically from time to time
TL_GET_RECENT_TIMELINE, // Recent timeline is shown when user clicks on action menu,...
// or after editing timeline
TL_GET_ONLY_OLD_PUBS, // Old publications are retrieved via AJAX
// when user clicks on link at bottom of timeline
} TL_WhatToGetFromTimeline_t;
// Timeline images will be saved with:
// - maximum width of TL_IMAGE_SAVED_MAX_HEIGHT
// - maximum height of TL_IMAGE_SAVED_MAX_HEIGHT
// - maintaining the original aspect ratio (aspect ratio recommended: 3:2)
#define TL_IMAGE_SAVED_MAX_WIDTH 768
#define TL_IMAGE_SAVED_MAX_HEIGHT 512
#define TL_IMAGE_SAVED_QUALITY 75 // 1 to 100
// in timeline posts, the quality should not be high in order to speed up the loading of images
/*****************************************************************************/
/****************************** Internal types *******************************/
/*****************************************************************************/
struct TL_Note
{
long NotCod;
TL_NoteType_t NoteType;
long UsrCod;
long HieCod; // Hierarchy code (institution/centre/degree/course)
long Cod; // Code of file, forum post, notice, timeline post...
bool Unavailable; // File, forum post, notice,... unavailable (removed)
time_t DateTimeUTC;
unsigned NumShared; // Number of times (users) this note has been shared
unsigned NumFavs; // Number of times (users) this note has been favourited
};
struct TL_Comment
{
long PubCod;
long UsrCod;
long NotCod; // Note code to which this comment belongs
time_t DateTimeUTC;
unsigned NumFavs; // Number of times (users) this comment has been favourited
char Content[Cns_MAX_BYTES_LONG_TEXT + 1];
struct Media Media;
};
typedef enum
{
TL_SHOW_A_FEW_USRS, // Show a few first favers/sharers
TL_SHOW_ALL_USRS, // Show all favers/sharers
} TL_HowMany_t;
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/************************* Internal global variables *************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/
static void TL_ShowTimelineGblHighlightingNot (long NotCod);
static void TL_ShowTimelineUsrHighlightingNot (long NotCod);
static void TL_GetAndShowOldTimeline (TL_TimelineUsrOrGbl_t TimelineUsrOrGbl);
static void TL_BuildQueryToGetTimeline (char **Query,
TL_TimelineUsrOrGbl_t TimelineUsrOrGbl,
TL_WhatToGetFromTimeline_t WhatToGetFromTimeline);
static long TL_GetPubCodFromSession (const char *FieldName);
static void TL_UpdateLastPubCodIntoSession (void);
static void TL_UpdateFirstPubCodIntoSession (long FirstPubCod);
static void TL_DropTemporaryTablesUsedToQueryTimeline (void);
static void TL_ShowTimeline (char *Query,
const char *Title,long NotCodToHighlight);
static void TL_PutIconsTimeline (void);
static void TL_FormStart (Act_Action_t ActionGbl,Act_Action_t ActionUsr);
static void TL_FormFavSha (Act_Action_t ActionGbl,Act_Action_t ActionUsr,
const char *ParamCod,
const char *Icon,const char *Title);
static void TL_PutFormWhichUsrs (void);
static void TL_PutParamWhichUsrs (void);
static void TL_GetParamsWhichUsrs (void);
static TL_WhichUsrs_t TL_GetWhichUsrsFromDB (void);
static void TL_SaveWhichUsersInDB (void);
static void TL_ShowWarningYouDontFollowAnyUser (void);
static void TL_InsertNewPubsInTimeline (char *Query);
static void TL_ShowOldPubsInTimeline (char *Query);
static void TL_GetDataOfPublicationFromRow (MYSQL_ROW row,struct TL_Publication *SocPub);
static void TL_PutLinkToViewNewPublications (void);
static void TL_PutLinkToViewOldPublications (void);
static void TL_WriteNote (const struct TL_Note *SocNot,
TL_TopMessage_t TopMessage,long UsrCod,
bool Highlight,
bool ShowNoteAlone);
static void TL_WriteTopMessage (TL_TopMessage_t TopMessage,long UsrCod);
static void TL_WriteAuthorNote (const struct UsrData *UsrDat);
static void TL_WriteDateTime (time_t TimeUTC);
static void TL_GetAndWritePost (long PstCod);
static void TL_PutFormGoToAction (const struct TL_Note *SocNot);
static void TL_GetNoteSummary (const struct TL_Note *SocNot,
char SummaryStr[Ntf_MAX_BYTES_SUMMARY + 1]);
static void TL_PublishNoteInTimeline (struct TL_Publication *SocPub);
static void TL_PutFormToWriteNewPost (void);
static void TL_PutTextarea (const char *Placeholder,const char *ClassTextArea);
static long TL_ReceivePost (void);
static void TL_PutIconToToggleCommentNote (const char UniqueId[Frm_MAX_BYTES_ID + 1]);
static void TL_PutIconCommentDisabled (void);
static void TL_PutHiddenFormToWriteNewCommentToNote (long NotCod,
const char IdNewComment[Frm_MAX_BYTES_ID + 1]);
static unsigned long TL_GetNumCommentsInNote (long NotCod);
static void TL_WriteCommentsInNote (const struct TL_Note *SocNot);
static void TL_WriteOneCommentInList (MYSQL_RES *mysql_res);
static void TL_PutIconToToggleComments (const char *UniqueId,
const char *Icon,const char *Text);
static void TL_WriteComment (struct TL_Comment *SocCom,
TL_TopMessage_t TopMessage,long UsrCod,
bool ShowCommentAlone);
static void TL_WriteAuthorComment (struct UsrData *UsrDat);
static void TL_PutFormToRemoveComment (long PubCod);
static void TL_PutDisabledIconShare (unsigned NumShared);
static void TL_PutDisabledIconFav (unsigned NumFavs);
static void TL_PutFormToSeeAllSharersNote (const struct TL_Note *SocNot,
TL_HowMany_t HowMany);
static void TL_PutFormToShaNote (const struct TL_Note *SocNot);
static void TL_PutFormToUnsNote (const struct TL_Note *SocNot);
static void TL_PutFormToSeeAllFaversNote (const struct TL_Note *SocNot,
TL_HowMany_t HowMany);
static void TL_PutFormToFavNote (const struct TL_Note *SocNot);
static void TL_PutFormToUnfNote (const struct TL_Note *SocNot);
static void TL_PutFormToSeeAllFaversComment (const struct TL_Comment *SocCom,
TL_HowMany_t HowMany);
static void TL_PutFormToFavComment (const struct TL_Comment *SocCom);
static void TL_PutFormToUnfComment (const struct TL_Comment *SocCom);
static void TL_PutFormToRemovePublication (long NotCod);
static void TL_PutHiddenParamNotCod (long NotCod);
static long TL_GetParamNotCod (void);
static long TL_GetParamPubCod (void);
static long TL_ReceiveComment (void);
static void TL_PutFormToShaUnsNote (const struct TL_Note *SocNot,
TL_HowMany_t HowMany);
static void TL_ShaNote (struct TL_Note *SocNot);
static void TL_UnsNote (struct TL_Note *SocNot);
static void TL_PutFormToFavUnfNote (const struct TL_Note *SocNot,
TL_HowMany_t HowMany);
static void TL_FavNote (struct TL_Note *SocNot);
static void TL_UnfNote (struct TL_Note *SocNot);
static void TL_PutFormToFavUnfComment (const struct TL_Comment *SocCom,
TL_HowMany_t HowMany);
static void TL_FavComment (struct TL_Comment *SocCom);
static void TL_UnfComment (struct TL_Comment *SocCom);
static void TL_CreateNotifToAuthor (long AuthorCod,long PubCod,
Ntf_NotifyEvent_t NotifyEvent);
static void TL_RequestRemovalNote (void);
static void TL_PutParamsRemoveNote (void);
static void TL_RemoveNote (void);
static void TL_RemoveNoteMediaAndDBEntries (struct TL_Note *SocNot);
static long TL_GetNotCodOfPublication (long PubCod);
static long TL_GetPubCodOfOriginalNote (long NotCod);
static void TL_RequestRemovalComment (void);
static void TL_PutParamsRemoveCommment (void);
static void TL_RemoveComment (void);
static void TL_RemoveCommentMediaAndDBEntries (long PubCod);
static bool TL_CheckIfNoteIsSharedByUsr (long NotCod,long UsrCod);
static bool TL_CheckIfNoteIsFavedByUsr (long NotCod,long UsrCod);
static bool TL_CheckIfCommIsFavedByUsr (long PubCod,long UsrCod);
static void TL_UpdateNumTimesANoteHasBeenShared (struct TL_Note *SocNot);
static void TL_GetNumTimesANoteHasBeenFav (struct TL_Note *SocNot);
static void TL_GetNumTimesACommHasBeenFav (struct TL_Comment *SocCom);
static void TL_ShowUsrsWhoHaveSharedNote (const struct TL_Note *SocNot,
TL_HowMany_t HowMany);
static void TL_ShowUsrsWhoHaveMarkedNoteAsFav (const struct TL_Note *SocNot,
TL_HowMany_t HowMany);
static void TL_ShowUsrsWhoHaveMarkedCommAsFav (const struct TL_Comment *SocCom,
TL_HowMany_t HowMany);
static void TL_ShowNumSharersOrFavers (unsigned NumUsrs);
static void TL_ShowSharersOrFavers (MYSQL_RES **mysql_res,
unsigned NumUsrs,unsigned NumFirstUsrs);
static void TL_GetDataOfNoteByCod (struct TL_Note *SocNot);
static void TL_GetDataOfCommByCod (struct TL_Comment *SocCom);
static void TL_GetDataOfPublicationFromRow (MYSQL_ROW row,struct TL_Publication *SocPub);
static void TL_GetDataOfNoteFromRow (MYSQL_ROW row,struct TL_Note *SocNot);
static TL_PubType_t TL_GetPubTypeFromStr (const char *Str);
static TL_NoteType_t TL_GetNoteTypeFromStr (const char *Str);
static void TL_GetDataOfCommentFromRow (MYSQL_ROW row,struct TL_Comment *SocCom);
static void TL_ResetNote (struct TL_Note *SocNot);
static void TL_ResetComment (struct TL_Comment *SocCom);
static void TL_ClearTimelineThisSession (void);
static void TL_AddNotesJustRetrievedToTimelineThisSession (void);
static void Str_AnalyzeTxtAndStoreNotifyEventToMentionedUsrs (long PubCod,const char *Txt);
/*****************************************************************************/
/************** Show timeline including all the users I follow ***************/
/*****************************************************************************/
void TL_ShowTimelineGbl1 (void)
{
/***** Mark all my notifications about timeline as seen *****/
TL_MarkMyNotifAsSeen ();
/***** Get which users *****/
TL_GetParamsWhichUsrs ();
/***** Save which users in database *****/
if (Gbl.Action.Act == ActSeeSocTmlGbl) // Only in action to see global timeline
TL_SaveWhichUsersInDB ();
}
void TL_ShowTimelineGbl2 (void)
{
long PubCod;
struct TL_Note SocNot;
struct UsrData UsrDat;
Ntf_NotifyEvent_t NotifyEvent;
const TL_TopMessage_t TopMessages[Ntf_NUM_NOTIFY_EVENTS] =
{
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_UNKNOWN
/* Course tab */
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_DOCUMENT_FILE
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_TEACHERS_FILE
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_SHARED_FILE
/* Assessment tab */
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_ASSIGNMENT
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_EXAM_ANNOUNCEMENT
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_MARKS_FILE
/* Users tab */
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_ENROLMENT_STD
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_ENROLMENT_TCH
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_ENROLMENT_REQUEST
/* Start tab */ // TODO: Move to top
TL_TOP_MESSAGE_COMMENTED, // Ntf_EVENT_TIMELINE_COMMENT
TL_TOP_MESSAGE_FAVED, // Ntf_EVENT_TIMELINE_FAV
TL_TOP_MESSAGE_SHARED, // Ntf_EVENT_TIMELINE_SHARE
TL_TOP_MESSAGE_MENTIONED, // Ntf_EVENT_TIMELINE_MENTION
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_FOLLOWER
/* Messages tab */
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_FORUM_POST_COURSE
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_FORUM_REPLY
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_NOTICE
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_MESSAGE
/* Statistics tab */
/* Profile tab */
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_SURVEY // TODO: Move to assessment tab (also necessary in database) !!!!!!!!!
TL_TOP_MESSAGE_NONE, // Ntf_EVENT_ENROLMENT_NET // TODO: Move to users tab (also necessary in database) !!!!!!!!!
};
/***** Initialize note code to -1 ==> no highlighted note *****/
SocNot.NotCod = -1L;
/***** Get parameter with the code of a publication *****/
// This parameter is optional. It can be provided by a notification.
// If > 0 ==> the note is shown highlighted above the timeline
PubCod = TL_GetParamPubCod ();
if (PubCod > 0)
/***** Get code of note from database *****/
SocNot.NotCod = TL_GetNotCodOfPublication (PubCod);
if (SocNot.NotCod > 0)
{
/* Get who did the action (publishing, commenting, faving, sharing, mentioning) */
Usr_GetParamOtherUsrCodEncrypted (&UsrDat);
/* Get what he/she did */
NotifyEvent = Ntf_GetParamNotifyEvent ();
/***** Show the note highlighted *****/
TL_GetDataOfNoteByCod (&SocNot);
TL_WriteNote (&SocNot,
TopMessages[NotifyEvent],UsrDat.UsrCod,
true,true);
}
/***** Show timeline with possible highlighted note *****/
TL_ShowTimelineGblHighlightingNot (SocNot.NotCod);
}
static void TL_ShowTimelineGblHighlightingNot (long NotCod)
{
extern const char *Txt_Timeline;
char *Query = NULL;
/***** Build query to get timeline *****/
TL_BuildQueryToGetTimeline (&Query,
TL_TIMELINE_GBL,
TL_GET_RECENT_TIMELINE);
/***** Show timeline *****/
TL_ShowTimeline (Query,Txt_Timeline,NotCod);
/***** Drop temporary tables *****/
TL_DropTemporaryTablesUsedToQueryTimeline ();
}
/*****************************************************************************/
/********************* Show timeline of a selected user **********************/
/*****************************************************************************/
void TL_ShowTimelineUsr (void)
{
TL_ShowTimelineUsrHighlightingNot (-1L);
}
static void TL_ShowTimelineUsrHighlightingNot (long NotCod)
{
extern const char *Txt_Timeline_OF_A_USER;
char *Query = NULL;
/***** Build query to show timeline with publications of a unique user *****/
TL_BuildQueryToGetTimeline (&Query,
TL_TIMELINE_USR,
TL_GET_RECENT_TIMELINE);
/***** Show timeline *****/
snprintf (Gbl.Title,sizeof (Gbl.Title),
Txt_Timeline_OF_A_USER,
Gbl.Usrs.Other.UsrDat.FirstName);
TL_ShowTimeline (Query,Gbl.Title,NotCod);
/***** Drop temporary tables *****/
TL_DropTemporaryTablesUsedToQueryTimeline ();
}
/*****************************************************************************/
/************** Refresh new publications in timeline via AJAX ****************/
/*****************************************************************************/
void TL_RefreshNewTimelineGbl (void)
{
char *Query = NULL;
if (Gbl.Session.IsOpen) // If session has been closed, do not write anything
{
/***** Get which users *****/
TL_GetParamsWhichUsrs ();
/***** Build query to get timeline *****/
TL_BuildQueryToGetTimeline (&Query,
TL_TIMELINE_GBL,
TL_GET_ONLY_NEW_PUBS);
/***** Show new timeline *****/
TL_InsertNewPubsInTimeline (Query);
/***** Drop temporary tables *****/
TL_DropTemporaryTablesUsedToQueryTimeline ();
}
}
/*****************************************************************************/
/**************** View old publications in timeline via AJAX *****************/
/*****************************************************************************/
void TL_RefreshOldTimelineGbl (void)
{
/***** Get which users *****/
TL_GetParamsWhichUsrs ();
/***** Show old publications *****/
TL_GetAndShowOldTimeline (TL_TIMELINE_GBL);
}
void TL_RefreshOldTimelineUsr (void)
{
/***** Get user whom profile is displayed *****/
if (Usr_GetParamOtherUsrCodEncryptedAndGetUsrData ()) // Existing user
/***** If user exists, show old publications *****/
TL_GetAndShowOldTimeline (TL_TIMELINE_USR);
}
/*****************************************************************************/
/**************** Get and show old publications in timeline ******************/
/*****************************************************************************/
static void TL_GetAndShowOldTimeline (TL_TimelineUsrOrGbl_t TimelineUsrOrGbl)
{
char *Query = NULL;
/***** Build query to get timeline *****/
TL_BuildQueryToGetTimeline (&Query,
TimelineUsrOrGbl,
TL_GET_ONLY_OLD_PUBS);
/***** Show old timeline *****/
TL_ShowOldPubsInTimeline (Query);
/***** Drop temporary tables *****/
TL_DropTemporaryTablesUsedToQueryTimeline ();
}
/*****************************************************************************/
/************ Mark all my notifications about timeline as seen ***************/
/*****************************************************************************/
// Must be executed as a priori function
void TL_MarkMyNotifAsSeen (void)
{
Ntf_MarkNotifAsSeen (Ntf_EVENT_TIMELINE_COMMENT,-1L,-1L,Gbl.Usrs.Me.UsrDat.UsrCod);
Ntf_MarkNotifAsSeen (Ntf_EVENT_TIMELINE_FAV ,-1L,-1L,Gbl.Usrs.Me.UsrDat.UsrCod);
Ntf_MarkNotifAsSeen (Ntf_EVENT_TIMELINE_SHARE ,-1L,-1L,Gbl.Usrs.Me.UsrDat.UsrCod);
Ntf_MarkNotifAsSeen (Ntf_EVENT_TIMELINE_MENTION,-1L,-1L,Gbl.Usrs.Me.UsrDat.UsrCod);
}
/*****************************************************************************/
/************************ Build query to get timeline ************************/
/*****************************************************************************/
#define TL_MAX_BYTES_SUBQUERY_ALREADY_EXISTS (256 - 1)
static void TL_BuildQueryToGetTimeline (char **Query,
TL_TimelineUsrOrGbl_t TimelineUsrOrGbl,
TL_WhatToGetFromTimeline_t WhatToGetFromTimeline)
{
char SubQueryPublishers[128];
char SubQueryRangeBottom[128];
char SubQueryRangeTop[128];
char SubQueryAlreadyExists[TL_MAX_BYTES_SUBQUERY_ALREADY_EXISTS + 1];
struct
{
long Top;
long Bottom;
} RangePubsToGet;
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumPubs;
unsigned NumPub;
long PubCod;
long NotCod;
const unsigned MaxPubsToGet[3] =
{
TL_MAX_NEW_PUBS_TO_GET_AND_SHOW, // TL_GET_ONLY_NEW_PUBS
TL_MAX_REC_PUBS_TO_GET_AND_SHOW, // TL_GET_RECENT_TIMELINE
TL_MAX_OLD_PUBS_TO_GET_AND_SHOW, // TL_GET_ONLY_OLD_PUBS
};
/***** Clear timeline for this session in database *****/
if (WhatToGetFromTimeline == TL_GET_RECENT_TIMELINE)
TL_ClearTimelineThisSession ();
/***** Drop temporary tables *****/
TL_DropTemporaryTablesUsedToQueryTimeline ();
/***** Create temporary table with publication codes *****/
DB_Query ("can not create temporary table",
"CREATE TEMPORARY TABLE pub_codes "
"(PubCod BIGINT NOT NULL,UNIQUE INDEX(PubCod)) ENGINE=MEMORY");
/***** Create temporary table with notes got in this execution *****/
DB_Query ("can not create temporary table",
"CREATE TEMPORARY TABLE not_codes "
"(NotCod BIGINT NOT NULL,INDEX(NotCod)) ENGINE=MEMORY");
/***** Create temporary table with notes already present in timeline for this session *****/
DB_Query ("can not create temporary table",
"CREATE TEMPORARY TABLE current_timeline "
"(NotCod BIGINT NOT NULL,INDEX(NotCod)) ENGINE=MEMORY"
" SELECT NotCod FROM social_timelines WHERE SessionId='%s'",
Gbl.Session.Id);
/***** Create temporary table and subquery with potential publishers *****/
switch (TimelineUsrOrGbl)
{
case TL_TIMELINE_USR: // Show the timeline of a user
sprintf (SubQueryPublishers,"PublisherCod=%ld AND ",
Gbl.Usrs.Other.UsrDat.UsrCod);
break;
case TL_TIMELINE_GBL: // Show the global timeline
switch (Gbl.Timeline.WhichUsrs)
{
case TL_USRS_FOLLOWED: // Show the timeline of the users I follow
DB_Query ("can not create temporary table",
"CREATE TEMPORARY TABLE publishers "
"(UsrCod INT NOT NULL,UNIQUE INDEX(UsrCod)) ENGINE=MEMORY"
" SELECT %ld AS UsrCod"
" UNION"
" SELECT FollowedCod AS UsrCod"
" FROM usr_follow WHERE FollowerCod=%ld",
Gbl.Usrs.Me.UsrDat.UsrCod,
Gbl.Usrs.Me.UsrDat.UsrCod);
sprintf (SubQueryPublishers,"social_pubs.PublisherCod=publishers.UsrCod AND ");
break;
case TL_USRS_ALL: // Show the timeline of all users
SubQueryPublishers[0] = '\0';
break;
default:
Lay_ShowErrorAndExit ("Wrong parameter which users.");
break;
}
break;
}
/***** Create subquery to get only notes not present in timeline *****/
switch (TimelineUsrOrGbl)
{
case TL_TIMELINE_USR: // Show the timeline of a user
switch (WhatToGetFromTimeline)
{
case TL_GET_ONLY_NEW_PUBS:
case TL_GET_RECENT_TIMELINE:
Str_Copy (SubQueryAlreadyExists,
" NotCod NOT IN"
" (SELECT NotCod FROM not_codes)",
TL_MAX_BYTES_SUBQUERY_ALREADY_EXISTS);
break;
case TL_GET_ONLY_OLD_PUBS:
Str_Copy (SubQueryAlreadyExists,
" NotCod NOT IN"
" (SELECT NotCod FROM current_timeline)",
TL_MAX_BYTES_SUBQUERY_ALREADY_EXISTS);
break;
}
break;
case TL_TIMELINE_GBL: // Show the timeline of the users I follow
switch (WhatToGetFromTimeline)
{
case TL_GET_ONLY_NEW_PUBS:
case TL_GET_RECENT_TIMELINE:
Str_Copy (SubQueryAlreadyExists,
" social_pubs.NotCod NOT IN"
" (SELECT NotCod FROM not_codes)",
TL_MAX_BYTES_SUBQUERY_ALREADY_EXISTS);
break;
case TL_GET_ONLY_OLD_PUBS:
Str_Copy (SubQueryAlreadyExists,
" social_pubs.NotCod NOT IN"
" (SELECT NotCod FROM current_timeline)",
TL_MAX_BYTES_SUBQUERY_ALREADY_EXISTS);
break;
}
break;
}
/***** Get the publications in timeline *****/
/* Initialize range of pubs:
social_pubs
_____
|_____|11
|_____|10
_|_____| 9 <-- RangePubsToGet.Top
Get / |_____| 8
pubs | |_____| 7
from < |_____| 6
this | |_____| 5
range \_|_____| 4
|_____| 3 <-- RangePubsToGet.Bottom
|_____| 2
|_____| 1
0
*/
RangePubsToGet.Top = 0; // +Infinite
RangePubsToGet.Bottom = 0; // -Infinite
switch (WhatToGetFromTimeline)
{
case TL_GET_ONLY_NEW_PUBS: // Get the publications (without limit) newer than LastPubCod
/* This query is made via AJAX automatically from time to time */
RangePubsToGet.Bottom = TL_GetPubCodFromSession ("LastPubCod");
break;
case TL_GET_RECENT_TIMELINE: // Get some limited recent publications
/* This is the first query to get initial timeline shown
==> no notes yet in current timeline table */
break;
case TL_GET_ONLY_OLD_PUBS: // Get some limited publications older than FirstPubCod
/* This query is made via AJAX
when I click in link to get old publications */
RangePubsToGet.Top = TL_GetPubCodFromSession ("FirstPubCod");
break;
}
/*
With the current approach, we select one by one
the publications and notes in a loop. In each iteration,
we get the more recent publication (original, shared or commment)
of every set of publications corresponding to the same note,
checking that the note is not already retrieved.
After getting a publication, its note code is stored
in order to not get it again.
As an alternative, we tried to get the maximum PubCod,
i.e more recent publication (original, shared or commment),
of every set of publications corresponding to the same note:
"SELECT MAX(PubCod) AS NewestPubCod FROM social_pubs ...
" GROUP BY NotCod ORDER BY NewestPubCod DESC LIMIT ..."
but this query is slow (several seconds) with a big table.
*/
for (NumPub = 0;
NumPub < MaxPubsToGet[WhatToGetFromTimeline];
NumPub++)
{
/* Create subqueries with range of publications to get from social_pubs */
if (RangePubsToGet.Bottom > 0)
switch (TimelineUsrOrGbl)
{
case TL_TIMELINE_USR: // Show the timeline of a user
sprintf (SubQueryRangeBottom,"PubCod>%ld AND ",
RangePubsToGet.Bottom);
break;
case TL_TIMELINE_GBL: // Show the global timeline
switch (Gbl.Timeline.WhichUsrs)
{
case TL_USRS_FOLLOWED: // Show the timeline of the users I follow
sprintf (SubQueryRangeBottom,"social_pubs.PubCod>%ld AND ",
RangePubsToGet.Bottom);
break;
case TL_USRS_ALL: // Show the timeline of all users
sprintf (SubQueryRangeBottom,"PubCod>%ld AND ",
RangePubsToGet.Bottom);
break;
default:
Lay_ShowErrorAndExit ("Wrong parameter which users.");
break;
}
break;
}
else
SubQueryRangeBottom[0] = '\0';
if (RangePubsToGet.Top > 0)
switch (TimelineUsrOrGbl)
{
case TL_TIMELINE_USR: // Show the timeline of a user
sprintf (SubQueryRangeTop,"PubCod<%ld AND ",
RangePubsToGet.Top);
break;
case TL_TIMELINE_GBL: // Show the global timeline
switch (Gbl.Timeline.WhichUsrs)
{
case TL_USRS_FOLLOWED: // Show the timeline of the users I follow
sprintf (SubQueryRangeTop,"social_pubs.PubCod<%ld AND ",
RangePubsToGet.Top);
break;
case TL_USRS_ALL: // Show the timeline of all users
sprintf (SubQueryRangeTop,"PubCod<%ld AND ",
RangePubsToGet.Top);
break;
default:
Lay_ShowErrorAndExit ("Wrong parameter which users.");
break;
}
break;
}
else
SubQueryRangeTop[0] = '\0';
/* Select the most recent publication from social_pubs */
NumPubs = 0; // Initialized to avoid warning
switch (TimelineUsrOrGbl)
{
case TL_TIMELINE_USR: // Show the timeline of a user
NumPubs =
(unsigned) DB_QuerySELECT (&mysql_res,"can not get publication",
"SELECT PubCod,NotCod"
" FROM social_pubs"
" WHERE %s%s%s%s"
" ORDER BY PubCod DESC LIMIT 1",
SubQueryRangeBottom,SubQueryRangeTop,
SubQueryPublishers,
SubQueryAlreadyExists);
break;
case TL_TIMELINE_GBL: // Show the global timeline
switch (Gbl.Timeline.WhichUsrs)
{
case TL_USRS_FOLLOWED: // Show the timeline of the users I follow
NumPubs =
(unsigned) DB_QuerySELECT (&mysql_res,"can not get publication",
"SELECT PubCod,NotCod"
" FROM social_pubs,publishers"
" WHERE %s%s%s%s"
" ORDER BY social_pubs.PubCod DESC LIMIT 1",
SubQueryRangeBottom,SubQueryRangeTop,
SubQueryPublishers,
SubQueryAlreadyExists);
break;
case TL_USRS_ALL: // Show the timeline of all users
NumPubs =
(unsigned) DB_QuerySELECT (&mysql_res,"can not get publication",
"SELECT PubCod,NotCod"
" FROM social_pubs"
" WHERE %s%s%s"
" ORDER BY PubCod DESC LIMIT 1",
SubQueryRangeBottom,SubQueryRangeTop,
SubQueryAlreadyExists);
break;
default:
Lay_ShowErrorAndExit ("Wrong parameter which users.");
break;
}
break;
}
if (NumPubs == 1)
{
/* Get code of publication */
row = mysql_fetch_row (mysql_res);
PubCod = Str_ConvertStrCodToLongCod (row[0]);
}
else
{
row = NULL;
PubCod = -1L;
}
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
if (PubCod > 0)
{
DB_QueryINSERT ("can not store publication code",
"INSERT INTO pub_codes SET PubCod=%ld",
PubCod);
RangePubsToGet.Top = PubCod; // Narrow the range for the next iteration
/* Get note code (row[1]) */
if (row)
{
NotCod = Str_ConvertStrCodToLongCod (row[1]);
DB_QueryINSERT ("can not store note code",
"INSERT INTO not_codes SET NotCod=%ld",
NotCod);
DB_QueryINSERT ("can not store note code",
"INSERT INTO current_timeline SET NotCod=%ld",
NotCod);
}
}
else // Nothing got ==> abort loop
break; // Last publication
}
/***** Update last publication code into session for next refresh *****/
// Do this inmediately after getting the publications codes...
// ...in order to not lose publications
TL_UpdateLastPubCodIntoSession ();
/***** Add notes just retrieved to current timeline for this session *****/
TL_AddNotesJustRetrievedToTimelineThisSession ();
/***** Build query to show timeline including the users I am following *****/
DB_BuildQuery (Query,
"SELECT PubCod," // row[0]
"NotCod," // row[1]
"PublisherCod," // row[2]
"PubType," // row[3]
"UNIX_TIMESTAMP(TimePublish)" // row[4]
" FROM social_pubs"
" WHERE PubCod IN "
"(SELECT PubCod"
" FROM pub_codes)"
" ORDER BY PubCod DESC");
}
/*****************************************************************************/
/************* Get last/first publication code stored in session *************/
/*****************************************************************************/
// FieldName can be:
// "LastPubCod"
// "FirstPubCod"
static long TL_GetPubCodFromSession (const char *FieldName)
{
extern const char *Txt_The_session_has_expired;
MYSQL_RES *mysql_res;
MYSQL_ROW row;
long PubCod;
/***** Get last publication code from database *****/
if (DB_QuerySELECT (&mysql_res,"can not get publication code from session",
"SELECT %s FROM sessions"
" WHERE SessionId='%s'",
FieldName,Gbl.Session.Id) != 1)
Lay_ShowErrorAndExit (Txt_The_session_has_expired);
/***** Get last publication code *****/
row = mysql_fetch_row (mysql_res);
if (sscanf (row[0],"%ld",&PubCod) != 1)
PubCod = 0;
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
return PubCod;
}
/*****************************************************************************/
/*********************** Update last publication code ************************/
/*****************************************************************************/
static void TL_UpdateLastPubCodIntoSession (void)
{
/***** Update last publication code *****/
DB_QueryUPDATE ("can not update last publication code into session",
"UPDATE sessions"
" SET LastPubCod="
"(SELECT IFNULL(MAX(PubCod),0) FROM social_pubs)"
" WHERE SessionId='%s'",
Gbl.Session.Id);
}
/*****************************************************************************/
/*********************** Update first publication code ***********************/
/*****************************************************************************/
static void TL_UpdateFirstPubCodIntoSession (long FirstPubCod)
{
/***** Update last publication code *****/
DB_QueryUPDATE ("can not update first publication code into session",
"UPDATE sessions SET FirstPubCod=%ld WHERE SessionId='%s'",
FirstPubCod,Gbl.Session.Id);
}
/*****************************************************************************/
/*************** Drop temporary tables used to query timeline ****************/
/*****************************************************************************/
static void TL_DropTemporaryTablesUsedToQueryTimeline (void)
{
DB_Query ("can not remove temporary tables",
"DROP TEMPORARY TABLE IF EXISTS"
" pub_codes,not_codes,publishers,current_timeline");
}
/*****************************************************************************/
/******************************* Show timeline *******************************/
/*****************************************************************************/
/* _____
/ |_____| just_now_timeline_list (Posts retrieved automatically
| |_____| via AJAX from time to time.
| |_____| They are transferred inmediately
| | to new_timeline_list.)
Hidden < __v__
| |_____| new_timeline_list (Posts retrieved but hidden.
| |_____| When user clicks to view them,
| |_____| they are transferred
\ |_____| to visible timeline_list.)
|
__v__
/ |_____| timeline_list (Posts visible on page)
| |_____|
Visible | |_____|
on < |_____|
page | |_____|
| |_____|
\ |_____|
^
__|__
/ |_____| old_timeline_list (Posts just retrieved via AJAX
| |_____| when user clicks "see more".
| |_____| They are transferred inmediately
Hidden < |_____| to timeline_list.)
| |_____|
| |_____|
\ |_____|
*/
static void TL_ShowTimeline (char *Query,
const char *Title,long NotCodToHighlight)
{
extern const char *Hlp_START_Timeline;
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned long NumPubsGot;
unsigned long NumPub;
struct TL_Publication SocPub;
struct TL_Note SocNot;
bool GlobalTimeline = (Gbl.Usrs.Other.UsrDat.UsrCod <= 0);
bool ItsMe = Usr_ItsMe (Gbl.Usrs.Other.UsrDat.UsrCod);
/***** Get publications from database *****/
NumPubsGot = DB_QuerySELECT (&mysql_res,"can not get timeline",
"%s",
Query);
/***** Start box *****/
Box_StartBox (NULL,Title,TL_PutIconsTimeline,
Hlp_START_Timeline,Box_NOT_CLOSABLE);
/***** Put form to select users whom public activity is displayed *****/
if (GlobalTimeline)
TL_PutFormWhichUsrs ();
/***** Form to write a new post *****/
if (GlobalTimeline || ItsMe)
TL_PutFormToWriteNewPost ();
/***** New publications refreshed dynamically via AJAX *****/
if (GlobalTimeline)
{
/* Link to view new publications via AJAX */
TL_PutLinkToViewNewPublications ();
/* Hidden list where insert just received (not visible) publications via AJAX */
fprintf (Gbl.F.Out,"");
/* Hidden list where insert new (not visible) publications via AJAX */
fprintf (Gbl.F.Out,"");
}
/***** List recent publications in timeline *****/
fprintf (Gbl.F.Out,"");
for (NumPub = 0, SocPub.PubCod = 0;
NumPub < NumPubsGot;
NumPub++)
{
/* Get data of publication */
row = mysql_fetch_row (mysql_res);
TL_GetDataOfPublicationFromRow (row,&SocPub);
/* Get data of note */
SocNot.NotCod = SocPub.NotCod;
TL_GetDataOfNoteByCod (&SocNot);
/* Write note */
TL_WriteNote (&SocNot,
SocPub.TopMessage,SocPub.PublisherCod,
SocNot.NotCod == NotCodToHighlight,
false);
}
fprintf (Gbl.F.Out,"
");
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
/***** Store first publication code into session *****/
TL_UpdateFirstPubCodIntoSession (SocPub.PubCod);
if (NumPubsGot == TL_MAX_REC_PUBS_TO_GET_AND_SHOW)
{
/***** Link to view old publications via AJAX *****/
TL_PutLinkToViewOldPublications ();
/***** Hidden list where insert old publications via AJAX *****/
fprintf (Gbl.F.Out,"");
}
/***** End box *****/
Box_EndBox ();
}
/*****************************************************************************/
/********************* Put contextual icons in timeline **********************/
/*****************************************************************************/
static void TL_PutIconsTimeline (void)
{
/***** Put icon to show a figure *****/
Gbl.Figures.FigureType = Fig_TIMELINE;
Fig_PutIconToShowFigure ();
}
/*****************************************************************************/
/***************** Start a form in global or user timeline *******************/
/*****************************************************************************/
static void TL_FormStart (Act_Action_t ActionGbl,Act_Action_t ActionUsr)
{
if (Gbl.Usrs.Other.UsrDat.UsrCod > 0)
{
Frm_StartFormAnchor (ActionUsr,"timeline");
Usr_PutParamOtherUsrCodEncrypted ();
}
else
{
Frm_StartForm (ActionGbl);
TL_PutParamWhichUsrs ();
}
}
/*****************************************************************************/
/******* Form to fav/unfav or share/unshare in global or user timeline *******/
/*****************************************************************************/
static void TL_FormFavSha (Act_Action_t ActionGbl,Act_Action_t ActionUsr,
const char *ParamCod,
const char *Icon,const char *Title)
{
char *OnSubmit;
/*
+---------------------------------------------------------------------------+
| div which content will be updated (parent of parent of form) |
| +---------------------+ +-------+ +-------------------------------------+ |
| | div (parent of form)| | div | | div for users | |
| | +-----------------+ | | for | | +------+ +------+ +------+ +------+ | |
| | | this form | | | num. | | | | | | | | | form | | |
| | | +-------------+ | | | of | | | user | | user | | user | | to | | |
| | | | fav icon | | | | users | | | 1 | | 2 | | 3 | | show | | |
| | | +-------------+ | | | | | | | | | | | | all | | |
| | +-----------------+ | | | | +------+ +------+ +------+ +------+ | |
| +---------------------+ +-------+ +-------------------------------------+ |
+---------------------------------------------------------------------------+
*/
/***** Form and icon to mark note as favourite *****/
/* Form with icon */
if (Gbl.Usrs.Other.UsrDat.UsrCod > 0)
{
if (asprintf (&OnSubmit,"updateDivFaversSharers(this,"
"'act=%ld&ses=%s&%s&OtherUsrCod=%s');"
" return false;", // return false is necessary to not submit form
Act_GetActCod (ActionUsr),
Gbl.Session.Id,
ParamCod,
Gbl.Usrs.Other.UsrDat.EncryptedUsrCod) < 0)
Lay_NotEnoughMemoryExit ();
Frm_StartFormUniqueAnchorOnSubmit (ActUnk,"timeline",OnSubmit);
}
else
{
if (asprintf (&OnSubmit,"updateDivFaversSharers(this,"
"'act=%ld&ses=%s&%s');"
" return false;", // return false is necessary to not submit form
Act_GetActCod (ActionGbl),
Gbl.Session.Id,
ParamCod) < 0)
Lay_NotEnoughMemoryExit ();
Frm_StartFormUniqueAnchorOnSubmit (ActUnk,NULL,OnSubmit);
}
Ico_PutIconLink (Icon,Title);
Frm_EndForm ();
/* Free allocated memory for subquery */
free ((void *) OnSubmit);
}
/*****************************************************************************/
/******** Show form to select users whom public activity is displayed ********/
/*****************************************************************************/
static void TL_PutFormWhichUsrs (void)
{
extern const char *Txt_TIMELINE_WHICH_USERS[TL_NUM_WHICH_USRS];
TL_WhichUsrs_t WhichUsrs;
static const char *Icon[TL_NUM_WHICH_USRS] =
{
NULL, // TL_USRS_UNKNOWN
"user-check.svg", // TL_USRS_FOLLOWED
"users.svg", // TL_USRS_ALL
};
/***** Setting selector for which users *****/
Set_StartSettingsHead ();
Set_StartOneSettingSelector ();
for (WhichUsrs = (TL_WhichUsrs_t) 1;
WhichUsrs < TL_NUM_WHICH_USRS;
WhichUsrs++)
{
fprintf (Gbl.F.Out,"",
WhichUsrs == Gbl.Timeline.WhichUsrs ? "PREF_ON" :
"PREF_OFF");
Frm_StartForm (ActSeeSocTmlGbl);
Par_PutHiddenParamUnsigned ("WhichUsrs",WhichUsrs);
Ico_PutSettingIconLink (Icon[WhichUsrs],Txt_TIMELINE_WHICH_USERS[WhichUsrs]);
Frm_EndForm ();
HTM_DIV_End ();
}
Set_EndOneSettingSelector ();
Set_EndSettingsHead ();
/***** Show warning if I do not follow anyone *****/
if (Gbl.Timeline.WhichUsrs == TL_USRS_FOLLOWED)
TL_ShowWarningYouDontFollowAnyUser ();
}
/*****************************************************************************/
/***** Put hidden parameter with which users to view in global timeline ******/
/*****************************************************************************/
static void TL_PutParamWhichUsrs (void)
{
Par_PutHiddenParamUnsigned ("WhichUsrs",Gbl.Timeline.WhichUsrs);
}
/*****************************************************************************/
/********* Get parameter with which users to view in global timeline *********/
/*****************************************************************************/
static void TL_GetParamsWhichUsrs (void)
{
/***** Get which users I want to see *****/
Gbl.Timeline.WhichUsrs = (TL_WhichUsrs_t)
Par_GetParToUnsignedLong ("WhichUsrs",
1,
TL_NUM_WHICH_USRS - 1,
(unsigned long) TL_USRS_UNKNOWN);
/***** If parameter WhichUsrs is not present, get it from database *****/
if (Gbl.Timeline.WhichUsrs == TL_USRS_UNKNOWN)
Gbl.Timeline.WhichUsrs = TL_GetWhichUsrsFromDB ();
/***** If parameter WhichUsrs is unknown, set it to default *****/
if (Gbl.Timeline.WhichUsrs == TL_USRS_UNKNOWN)
Gbl.Timeline.WhichUsrs = TL_DEFAULT_WHICH_USRS;
}
/*****************************************************************************/
/********** Get user's last data from database giving a user's code **********/
/*****************************************************************************/
static TL_WhichUsrs_t TL_GetWhichUsrsFromDB (void)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned UnsignedNum;
TL_WhichUsrs_t WhichUsrs = TL_USRS_UNKNOWN;
/***** Get which users from database *****/
if (DB_QuerySELECT (&mysql_res,"can not get timeline users from user's last data",
"SELECT TimelineUsrs" // row[0]
" FROM usr_last WHERE UsrCod=%ld",
Gbl.Usrs.Me.UsrDat.UsrCod) == 1)
{
row = mysql_fetch_row (mysql_res);
/* Get which users */
if (sscanf (row[0],"%u",&UnsignedNum) == 1)
if (UnsignedNum < TL_NUM_WHICH_USRS)
WhichUsrs = (TL_WhichUsrs_t) UnsignedNum;
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
return WhichUsrs;
}
/*****************************************************************************/
/********************** Save which users into database ***********************/
/*****************************************************************************/
static void TL_SaveWhichUsersInDB (void)
{
if (Gbl.Usrs.Me.Logged)
{
if (Gbl.Timeline.WhichUsrs == TL_USRS_UNKNOWN)
Gbl.Timeline.WhichUsrs = TL_DEFAULT_WHICH_USRS;
/***** Update which users in database *****/
// WhichUsrs is stored in usr_last for next time I log in
DB_QueryUPDATE ("can not update timeline users in user's last data",
"UPDATE usr_last SET TimelineUsrs=%u"
" WHERE UsrCod=%ld",
(unsigned) Gbl.Timeline.WhichUsrs,
Gbl.Usrs.Me.UsrDat.UsrCod);
}
}
/*****************************************************************************/
/********* Get parameter with which users to view in global timeline *********/
/*****************************************************************************/
static void TL_ShowWarningYouDontFollowAnyUser (void)
{
extern const char *Txt_You_dont_follow_any_user;
unsigned NumFollowing;
unsigned NumFollowers;
/***** Check if I follow someone *****/
Fol_GetNumFollow (Gbl.Usrs.Me.UsrDat.UsrCod,&NumFollowing,&NumFollowers);
if (!NumFollowing)
{
/***** Show warning if I do not follow anyone *****/
Ale_ShowAlert (Ale_WARNING,Txt_You_dont_follow_any_user);
/***** Put link to show users to follow *****/
fprintf (Gbl.F.Out,"