// swad_notification.c: notifications about events, sent by email

/*
    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-2025 Antonio Caas 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 <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/********************************* Headers ***********************************/
/*****************************************************************************/

#define _GNU_SOURCE 		// For asprintf
#include <stddef.h>		// For NULL
#include <stdio.h>		// For asprintf
#include <stdlib.h>		// For system
#include <string.h>
#include <sys/wait.h>		// For the macro WEXITSTATUS
#include <unistd.h>		// For unlink

#include "swad_action.h"
#include "swad_action_list.h"
#include "swad_admin_database.h"
#include "swad_assignment_database.h"
#include "swad_box.h"
#include "swad_browser_database.h"
#include "swad_call_for_exam.h"
#include "swad_config.h"
#include "swad_config.h"
#include "swad_database.h"
#include "swad_enrolment.h"
#include "swad_enrolment_database.h"
#include "swad_error.h"
#include "swad_figure.h"
#include "swad_follow.h"
#include "swad_form.h"
#include "swad_forum.h"
#include "swad_forum_database.h"
#include "swad_global.h"
#include "swad_group_database.h"
#include "swad_hierarchy.h"
#include "swad_hierarchy_type.h"
#include "swad_HTML.h"
#include "swad_mark.h"
#include "swad_message.h"
#include "swad_message_database.h"
#include "swad_notice.h"
#include "swad_notification.h"
#include "swad_notification_database.h"
#include "swad_parameter_code.h"
#include "swad_setting.h"
#include "swad_setting_database.h"
#include "swad_survey.h"
#include "swad_survey_database.h"
#include "swad_timeline.h"
#include "swad_timeline_database.h"
#include "swad_timeline_notification.h"
#include "swad_timeline_publication.h"
#include "swad_user_database.h"

/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/

extern struct Globals Gbl;

/*****************************************************************************/
/****************************** Public constants *****************************/
/*****************************************************************************/

// strings are limited to Ntf_MAX_BYTES_NOTIFY_EVENT characters

const char *Ntf_WSNotifyEvents[Ntf_NUM_NOTIFY_EVENTS] =
  {
   [Ntf_EVENT_UNKNOWN          ] = "unknown",
   /* Start tab */
   [Ntf_EVENT_TML_COMMENT      ] = "timelineComment",
   [Ntf_EVENT_TML_FAV          ] = "timelineFav",
   [Ntf_EVENT_TML_SHARE        ] = "timelineShare",
   [Ntf_EVENT_TML_MENTION      ] = "timelineMention",
   [Ntf_EVENT_FOLLOWER         ] = "follower",
   /* System tab */
   /* Country tab */
   /* Institution tab */
   /* Center tab */
   /* Degree tab */
   /* Course tab */
   /* Assessment tab */
   [Ntf_EVENT_ASSIGNMENT       ] = "assignment",
   [Ntf_EVENT_CALL_FOR_EXAM    ] = "examAnnouncement",
   /* Files tab */
   [Ntf_EVENT_DOCUMENT_FILE    ] = "documentFile",
   [Ntf_EVENT_TEACHERS_FILE    ] = "teachersFile",
   [Ntf_EVENT_SHARED_FILE      ] = "sharedFile",
   [Ntf_EVENT_MARKS_FILE       ] = "marksFile",
   /* Users tab */
   [Ntf_EVENT_ENROLMENT_STD    ] = "enrollmentStudent",
   [Ntf_EVENT_ENROLMENT_NET    ] = "enrolmentNonEditingTeacher",
   [Ntf_EVENT_ENROLMENT_TCH    ] = "enrollmentTeacher",
   [Ntf_EVENT_ENROLMENT_REQUEST] = "enrollmentRequest",
   /* Messages tab */
   [Ntf_EVENT_NOTICE           ] = "notice",
   [Ntf_EVENT_FORUM_POST_COURSE] = "forumPostCourse",
   [Ntf_EVENT_FORUM_REPLY      ] = "forumReply",
   [Ntf_EVENT_MESSAGE          ] = "message",
   /* Analytics tab */
   [Ntf_EVENT_SURVEY           ] = "survey",
   /* Profile tab */
  };

static Act_Action_t Ntf_DefaultActions[Ntf_NUM_NOTIFY_EVENTS] =
  {
   [Ntf_EVENT_UNKNOWN          ] = ActUnk,
   /* Start tab */
   [Ntf_EVENT_TML_COMMENT      ] = ActSeeGblTL,
   [Ntf_EVENT_TML_FAV          ] = ActSeeGblTL,
   [Ntf_EVENT_TML_SHARE        ] = ActSeeGblTL,
   [Ntf_EVENT_TML_MENTION      ] = ActSeeGblTL,
   [Ntf_EVENT_FOLLOWER         ] = ActSeeFlr,
   /* System tab */
   /* Country tab */
   /* Institution tab */
   /* Center tab */
   /* Degree tab */
   /* Course tab */
   /* Assessment tab */
   [Ntf_EVENT_ASSIGNMENT       ] = ActSeeAllAsg,
   [Ntf_EVENT_CALL_FOR_EXAM    ] = ActSeeAllCfe,
   /* Files tab */
   [Ntf_EVENT_DOCUMENT_FILE    ] = ActSeeAdmDocCrsGrp,
   [Ntf_EVENT_TEACHERS_FILE    ] = ActAdmTchCrsGrp,
   [Ntf_EVENT_SHARED_FILE      ] = ActAdmShaCrsGrp,
   [Ntf_EVENT_MARKS_FILE       ] = ActSeeAdmMrk,
   /* Users tab */
   [Ntf_EVENT_ENROLMENT_STD    ] = ActReqAccEnrStd,
   [Ntf_EVENT_ENROLMENT_NET    ] = ActReqAccEnrNET,
   [Ntf_EVENT_ENROLMENT_TCH    ] = ActReqAccEnrTch,
   [Ntf_EVENT_ENROLMENT_REQUEST] = ActSeeSignUpReq,
   /* Messages tab */
   [Ntf_EVENT_NOTICE           ] = ActSeeOneNot,
   [Ntf_EVENT_FORUM_POST_COURSE] = ActSeeFor,
   [Ntf_EVENT_FORUM_REPLY      ] = ActSeeFor,
   [Ntf_EVENT_MESSAGE          ] = ActExpRcvMsg,
   /* Analytics tab */
   [Ntf_EVENT_SURVEY           ] = ActSeeAllSvy,
   /* Profile tab */
  };

/*****************************************************************************/
/***************************** Private constants *****************************/
/*****************************************************************************/

// Notify me notification events
static const char *Ntf_ParNotifMeAboutNotifyEvents[Ntf_NUM_NOTIFY_EVENTS] =
  {
   [Ntf_EVENT_UNKNOWN          ] = "NotifyNtfEventUnknown",
   /* Start tab */
   [Ntf_EVENT_TML_COMMENT      ] = "NotifyNtfEventTimelineComment",
   [Ntf_EVENT_TML_FAV          ] = "NotifyNtfEventTimelineFav",
   [Ntf_EVENT_TML_SHARE        ] = "NotifyNtfEventTimelineShare",
   [Ntf_EVENT_TML_MENTION      ] = "NotifyNtfEventTimelineMention",
   [Ntf_EVENT_FOLLOWER         ] = "NotifyNtfEventFollower",
   /* System tab */
   /* Country tab */
   /* Institution tab */
   /* Center tab */
   /* Degree tab */
   /* Course tab */
   /* Assessment tab */
   [Ntf_EVENT_ASSIGNMENT       ] = "NotifyNtfEventAssignment",
   [Ntf_EVENT_CALL_FOR_EXAM    ] = "NotifyNtfEventExamAnnouncement",
   /* Files tab */
   [Ntf_EVENT_DOCUMENT_FILE    ] = "NotifyNtfEventDocumentFile",
   [Ntf_EVENT_TEACHERS_FILE    ] = "NotifyNtfEventTeachersFile",
   [Ntf_EVENT_SHARED_FILE      ] = "NotifyNtfEventSharedFile",
   [Ntf_EVENT_MARKS_FILE       ] = "NotifyNtfEventMarksFile",
   /* Messages tab */
   [Ntf_EVENT_NOTICE           ] = "NotifyNtfEventNotice",
   [Ntf_EVENT_FORUM_POST_COURSE] = "NotifyNtfEventForumPostCourse",
   [Ntf_EVENT_FORUM_REPLY      ] = "NotifyNtfEventForumReply",
   [Ntf_EVENT_MESSAGE          ] = "NotifyNtfEventMessage",
   /* Users tab */
   [Ntf_EVENT_ENROLMENT_STD    ] = "NotifyNtfEventEnrolmentStudent",
   [Ntf_EVENT_ENROLMENT_NET    ] = "NotifyNtfEventEnrolmentNonEditingTeacher",
   [Ntf_EVENT_ENROLMENT_TCH    ] = "NotifyNtfEventEnrolmentTeacher",
   [Ntf_EVENT_ENROLMENT_REQUEST] = "NotifyNtfEventEnrolmentRequest",
   /* Analytics tab */
   [Ntf_EVENT_SURVEY           ] = "NotifyNtfEventSurvey",
   /* Profile tab */
  };

// Email me about notification events
static const char *Ntf_ParEmailMeAboutNotifyEvents[Ntf_NUM_NOTIFY_EVENTS] =
  {
   [Ntf_EVENT_UNKNOWN          ] = "EmailNtfEventUnknown",
   /* Start tab */
   [Ntf_EVENT_TML_COMMENT      ] = "EmailNtfEventTimelineComment",
   [Ntf_EVENT_TML_FAV          ] = "EmailNtfEventTimelineFav",
   [Ntf_EVENT_TML_SHARE        ] = "EmailNtfEventTimelineShare",
   [Ntf_EVENT_TML_MENTION      ] = "EmailNtfEventTimelineMention",
   [Ntf_EVENT_FOLLOWER         ] = "EmailNtfEventSocialFollower",
   /* System tab */
   /* Country tab */
   /* Institution tab */
   /* Center tab */
   /* Degree tab */
   /* Course tab */
   /* Assessment tab */
   [Ntf_EVENT_ASSIGNMENT       ] = "EmailNtfEventAssignment",
   [Ntf_EVENT_CALL_FOR_EXAM    ] = "EmailNtfEventExamAnnouncement",
   /* Files tab */
   [Ntf_EVENT_DOCUMENT_FILE    ] = "EmailNtfEventDocumentFile",
   [Ntf_EVENT_TEACHERS_FILE    ] = "EmailNtfEventTeachersFile",
   [Ntf_EVENT_SHARED_FILE      ] = "EmailNtfEventSharedFile",
   [Ntf_EVENT_MARKS_FILE       ] = "EmailNtfEventMarksFile",
   /* Messages tab */
   [Ntf_EVENT_NOTICE           ] = "EmailNtfEventNotice",
   [Ntf_EVENT_FORUM_POST_COURSE] = "EmailNtfEventForumPostCourse",
   [Ntf_EVENT_FORUM_REPLY      ] = "EmailNtfEventForumReply",
   [Ntf_EVENT_MESSAGE          ] = "EmailNtfEventMessage",
   /* Users tab */
   [Ntf_EVENT_ENROLMENT_STD    ] = "EmailNtfEventEnrolmentStudent",
   [Ntf_EVENT_ENROLMENT_NET    ] = "EmailNtfEventEnrolmentNonEditingTeacher",
   [Ntf_EVENT_ENROLMENT_TCH    ] = "EmailNtfEventEnrolmentTeacher",
   [Ntf_EVENT_ENROLMENT_REQUEST] = "EmailNtfEventEnrolmentRequest",
   /* Analytics tab */
   [Ntf_EVENT_SURVEY           ] = "EmailNtfEventSurvey",
   /* Profile tab */
  };

// Icons for notification events
static const char *Ntf_Icons[Ntf_NUM_NOTIFY_EVENTS] =
  {
   [Ntf_EVENT_UNKNOWN          ] = "question.svg",
   /* Start tab */
   [Ntf_EVENT_TML_COMMENT      ] = "comment-dots.svg",
   [Ntf_EVENT_TML_FAV          ] = "star.svg",
   [Ntf_EVENT_TML_SHARE        ] = "share-alt.svg",
   [Ntf_EVENT_TML_MENTION      ] = "at.svg",
   [Ntf_EVENT_FOLLOWER         ] = "user-plus.svg",
   /* System tab */
   /* Country tab */
   /* Institution tab */
   /* Center tab */
   /* Degree tab */
   /* Course tab */
   /* Assessment tab */
   [Ntf_EVENT_ASSIGNMENT       ] = "edit.svg",
   [Ntf_EVENT_CALL_FOR_EXAM    ] = "bullhorn.svg",
   /* Files tab */
   [Ntf_EVENT_DOCUMENT_FILE    ] = "file.svg",
   [Ntf_EVENT_TEACHERS_FILE    ] = "file.svg",
   [Ntf_EVENT_SHARED_FILE      ] = "file.svg",
   [Ntf_EVENT_MARKS_FILE       ] = "clipboard-list.svg",
   /* Messages tab */
   [Ntf_EVENT_NOTICE           ] = "sticky-note.svg",
   [Ntf_EVENT_FORUM_POST_COURSE] = "comments.svg",
   [Ntf_EVENT_FORUM_REPLY      ] = "comments.svg",
   [Ntf_EVENT_MESSAGE          ] = "envelope.svg",
   /* Users tab */
   [Ntf_EVENT_ENROLMENT_STD    ] = "user.svg",
   [Ntf_EVENT_ENROLMENT_NET    ] = "person-chalkboard.svg",
   [Ntf_EVENT_ENROLMENT_TCH    ] = "person-chalkboard.svg",
   [Ntf_EVENT_ENROLMENT_REQUEST] = "hand-point-up.svg",
   /* Analytics tab */
   [Ntf_EVENT_SURVEY           ] = "poll.svg",
   /* Profile tab */
  };

/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/

static void Ntf_PutContextualLinks (bool AllNotifications,
				    unsigned NumNotifications);
static void Ntf_PutIconsNotif (__attribute__((unused)) void *Args);
static void Ntf_WriteHeading (void);
static void Ntf_GetNotif (MYSQL_RES *mysql_res,
			  Ntf_NotifyEvent_t *NotifyEvent,
			  struct Usr_Data *UsrDat,
			  struct Hie_Node Hie[Hie_NUM_LEVELS],
			  long *Cod,time_t *DateTimeUTC,
			  Ntf_Status_t *Status);
static void Ntf_WriteNotif (Ntf_NotifyEvent_t NotifyEvent,
			    struct Usr_Data *UsrDat,
			    const struct Hie_Node Hie[Hie_NUM_LEVELS],
			    long Cod,time_t DateTimeUTC,
			    Ntf_Status_t Status);

static bool Ntf_GetAllNotificationsFromForm (void);

static Act_Action_t Ntf_StartFormGoToAction (Ntf_NotifyEvent_t NotifyEvent,
                                             long CrsCod,const struct Usr_Data *UsrDat,long Cod,
                                             const struct For_Forums *Forums);
static void Ntf_PutParNotifyEvent (Ntf_NotifyEvent_t NotifyEvent);

static void Ntf_SendPendingNotifByEMailToOneUsr (const struct Usr_Data *ToUsrDat,
                                                 unsigned *NumNotif,
                                                 unsigned *NumMails);
static void Ntf_GetNumNotifSent (long DegCod,long CrsCod,
                                 Ntf_NotifyEvent_t NotifyEvent,
                                 unsigned *NumEvents,unsigned *NumMails);
static void Ntf_UpdateNumNotifSent (long DegCod,long CrsCod,
                                    Ntf_NotifyEvent_t NotifyEvent,
                                    unsigned NumEvents,unsigned NumMails);

static void Ntf_GetParsNotifyEvents (void);

/*****************************************************************************/
/*************************** Show my notifications ***************************/
/*****************************************************************************/

void Ntf_ShowMyNotifications (void)
  {
   extern const char *Hlp_START_Notifications;
   extern const char *Txt_Notifications;
   extern const char *Txt_You_have_no_notifications;
   extern const char *Txt_You_have_no_unread_notifications;
   MYSQL_RES *mysql_res;
   unsigned NumNotif;
   unsigned NumNotifications;
   bool AllNotifications;
   Ntf_NotifyEvent_t NotifyEvent = (Ntf_NotifyEvent_t) 0;	// Initialized to avoid warning
   struct Usr_Data UsrDat;
   struct Hie_Node Hie[Hie_NUM_LEVELS];
   long Cod;
   time_t DateTimeUTC;	// Date-time of the event
   Ntf_Status_t Status;

   /***** Get my notifications from database *****/
   AllNotifications = Ntf_GetAllNotificationsFromForm ();
   NumNotifications = Ntf_DB_GetMyNotifications (&mysql_res,AllNotifications);

   /***** Contextual menu *****/
   Ntf_PutContextualLinks (AllNotifications,NumNotifications);

   /***** Begin box *****/
   Box_BoxBegin (Txt_Notifications,Ntf_PutIconsNotif,NULL,
                 Hlp_START_Notifications,Box_NOT_CLOSABLE);

      /***** List my notifications *****/
      if (NumNotifications)	// Notifications found
	{
	 /***** Initialize structure with user's data *****/
	 Usr_UsrDataConstructor (&UsrDat);

	 /***** Begin table *****/
	 HTM_TABLE_BeginWideMarginPadding (2);

	    /***** Heading *****/
	    Ntf_WriteHeading ();

	    /***** List notifications one by one *****/
	    for (NumNotif = 0;
		 NumNotif < NumNotifications;
		 NumNotif++)
	      {
	       /* Get notification */
	       Ntf_GetNotif (mysql_res,
			     &NotifyEvent,&UsrDat,Hie,&Cod,&DateTimeUTC,&Status);

	       /* Write row for this notification */
	       Ntf_WriteNotif (NotifyEvent,&UsrDat,Hie,Cod,DateTimeUTC,Status);
	      }

	 /***** End table *****/
	 HTM_TABLE_End ();

	 /***** Free memory used for user's data *****/
	 Usr_UsrDataDestructor (&UsrDat);
	}
      else
	 Ale_ShowAlert (Ale_INFO,AllNotifications ? Txt_You_have_no_notifications :
						    Txt_You_have_no_unread_notifications);

   /***** End box *****/
   Box_BoxEnd ();

   /***** Free structure that stores the query result *****/
   DB_FreeMySQLResult (&mysql_res);

   /***** Reset to 0 the number of new notifications *****/
   Ntf_DB_UpdateMyLastAccessToNotifications ();
  }

/*****************************************************************************/
/*************************** Put contextual links ****************************/
/*****************************************************************************/

static void Ntf_PutContextualLinks (bool AllNotifications,
				    unsigned NumNotifications)
  {
   extern const char *Txt_Show_all_notifications;
   extern const char *Txt_Show_all_NOTIFICATIONS;
   extern const char *Txt_Mark_all_NOTIFICATIONS_as_read;
   extern const char *Txt_Domains;

   Mnu_ContextMenuBegin ();

      /***** Show all notifications *****/
      Lay_PutContextualCheckbox (ActSeeNtf,
				 NULL,NULL,
				 "All",
				 (AllNotifications ? HTM_CHECKED :
						     HTM_NO_ATTR) | HTM_SUBMIT_ON_CHANGE,
				 Txt_Show_all_notifications,
				 Txt_Show_all_NOTIFICATIONS);

      /***** Mark notifications as read *****/
      if (NumNotifications)	// TODO: Show message only when I don't have notificacions at all
	 Lay_PutContextualLinkIconText (ActMrkNtfSee,NULL,
					NULL,NULL,
					"eye.svg",Ico_BLACK,
					Txt_Mark_all_NOTIFICATIONS_as_read,NULL);

      /***** View allowed mail domains *****/
      Lay_PutContextualLinkIconText (ActSeeMai,NULL,
				     NULL,NULL,
				     "envelope.svg",Ico_BLACK,
				     Txt_Domains,NULL);

   Mnu_ContextMenuEnd ();
  }

/*****************************************************************************/
/****************** Put contextual icons in notifications ********************/
/*****************************************************************************/

static void Ntf_PutIconsNotif (__attribute__((unused)) void *Args)
  {
   /***** Edit notification settings *****/
   if (Gbl.Action.Act != ActReqEdiSet)
      Ico_PutContextualIconToConfigure (ActReqEdiSet,Ntf_NOTIFICATIONS_ID,
					NULL,NULL);

   /***** Put icon to show a figure *****/
   Fig_PutIconToShowFigure (Fig_NOTIFY_EVENTS);
  }

/*****************************************************************************/
/**************************** Write table heading ****************************/
/*****************************************************************************/

static void Ntf_WriteHeading (void)
  {
   extern const char *Txt_MSG_From_USER;
   extern const char *Txt_Location;
   extern const char *Txt_Date;
   extern const char *Txt_Email;

   HTM_TR_Begin (NULL);
      HTM_TH_Empty (2);
      HTM_TH (Txt_MSG_From_USER,HTM_HEAD_LEFT );
      HTM_TH (Txt_Location,HTM_HEAD_LEFT );
      HTM_TH (Txt_Date    ,HTM_HEAD_RIGHT);
      HTM_TH (Txt_Email   ,HTM_HEAD_LEFT );
   HTM_TR_End ();
  }

/*****************************************************************************/
/****************** Get a notification from database result ******************/
/*****************************************************************************/

static void Ntf_GetNotif (MYSQL_RES *mysql_res,
			  Ntf_NotifyEvent_t *NotifyEvent,
			  struct Usr_Data *UsrDat,
			  struct Hie_Node Hie[Hie_NUM_LEVELS],
			  long *Cod,time_t *DateTimeUTC,
			  Ntf_Status_t *Status)
  {
   extern Err_SuccessOrError_t (*Hie_GetDataByCod[Hie_NUM_LEVELS]) (struct Hie_Node *Node);
   MYSQL_ROW row;
   __attribute__((unused)) Exi_Exist_t UsrExists;
   Hie_Level_t HieLvl;
   __attribute__((unused)) Err_SuccessOrError_t SuccessOrError;
   unsigned Col;

   /***** Get next notification *****/
   row = mysql_fetch_row (mysql_res);

   /***** Get event type (row[0]) *****/
   *NotifyEvent = Ntf_GetNotifyEventFromStr (row[0]);

   /***** Get (from) user code (row[1]) *****/
   UsrDat->UsrCod = Str_ConvertStrCodToLongCod (row[1]);
   UsrExists = Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (UsrDat,	// Get user's data from database
							Usr_DONT_GET_PREFS,
							Usr_DONT_GET_ROLE_IN_CRS);

   /***** Get institution code, center code, degree code and course code
          (row[2], row[3], row[4] and row[5]) *****/
   for (HieLvl  = Hie_INS, Col = 2;
	HieLvl <= Hie_CRS;
	HieLvl++, Col++)
     {
      Hie[HieLvl].HieCod = Str_ConvertStrCodToLongCod (row[Col]);
      SuccessOrError = Hie_GetDataByCod[HieLvl] (&Hie[HieLvl]);
     }

   /***** Get message/post/... code (row[6]) *****/
   *Cod = Str_ConvertStrCodToLongCod (row[6]);

   /***** Get time of the event (row[7]) *****/
   *DateTimeUTC = Dat_GetUNIXTimeFromStr (row[7]);

   /***** Get status (row[8]) *****/
   if (sscanf (row[8],"%u",Status) != 1)
      Err_WrongStatusExit ();
  }

/*****************************************************************************/
/******************* Write a table row for a notification ********************/
/*****************************************************************************/

static void Ntf_WriteNotif (Ntf_NotifyEvent_t NotifyEvent,
			    struct Usr_Data *UsrDat,
			    const struct Hie_Node Hie[Hie_NUM_LEVELS],
			    long Cod,time_t DateTimeUTC,
			    Ntf_Status_t Status)
  {
   extern const char *Txt_NOTIFY_EVENTS_SINGULAR[Ntf_NUM_NOTIFY_EVENTS];
   extern const char *Txt_Forum;
   extern const char *Txt_NOTIFICATION_STATUS[Ntf_NUM_STATUS_TXT];
   extern const char *Txt_HIERARCHY_SINGUL_Abc[Hie_NUM_LEVELS];
   extern const char *Txt_You_have_no_notifications;
   extern const char *Txt_You_have_no_unread_notifications;
   char SummaryStr[Ntf_MAX_BYTES_SUMMARY + 1];
   char *ContentStr;
   struct
     {
      const char *Txt;
      const char *Link;
      const char *Author;
      const char *Bg;
     } Class;
   Frm_PutForm_t PutForm;
   Act_Action_t Action = ActUnk;
   Hie_Level_t HieLvl;
   Ntf_StatusTxt_t StatusTxt;
   struct For_Forums Forums;
   char ForumName[For_MAX_BYTES_FORUM_NAME + 1];

   if (Status & Ntf_STATUS_BIT_REMOVED)	// The source of the notification was removed
     {
      Class.Txt    = "MSG_TIT_REM";
      Class.Link   = "BT_LINK MSG_TIT_REM";
      Class.Author = "MSG_AUT_LIGHT";
      Class.Bg     = "MSG_BG_REM";
      PutForm = Frm_DONT_PUT_FORM;
     }
   else
     {
      if (Status & Ntf_STATUS_BIT_READ)	// I have already seen the source of the notification
	{
	 Class.Txt    = "MSG_TIT";
	 Class.Link   = "LT BT_LINK MSG_TIT";
	 Class.Author = "MSG_AUT";
	 Class.Bg     = "MSG_BG";
	}
      else					// I have not seen the source of the notification
	{
	 Class.Txt    = "MSG_TIT_NEW";
	 Class.Link   = "LT BT_LINK MSG_TIT_NEW";
	 Class.Author = "MSG_AUT_NEW";
	 Class.Bg     = "MSG_BG_NEW";
	}
      PutForm = Frm_PUT_FORM;
     }

   /***** Write row for this notification *****/
   /* Write event icon */
   HTM_TR_Begin (NULL);

      HTM_TD_Begin ("class=\"LT %s_%s\" style=\"width:25px;\"",
		    Class.Bg,The_GetSuffix ());
	 if (PutForm == Frm_PUT_FORM)
	   {
	    Action = Ntf_StartFormGoToAction (NotifyEvent,Hie[Hie_CRS].HieCod,UsrDat,Cod,&Forums);
	    PutForm = Frm_CheckIfInside () == Frm_INSIDE_FORM ? Frm_PUT_FORM :
								Frm_DONT_PUT_FORM;
	   }
	 switch (PutForm)
	   {
	    case Frm_PUT_FORM:
	       Ico_PutIconLink (Ntf_Icons[NotifyEvent],Ico_BLACK,Action);
	       Frm_EndForm ();
	       break;
	    case Frm_DONT_PUT_FORM:
	    default:
	       Ico_PutIconOff (Ntf_Icons[NotifyEvent],Ico_BLACK,
			       Txt_NOTIFY_EVENTS_SINGULAR[NotifyEvent]);
	       break;
	   }
      HTM_TD_End ();

      /* Write event type */
      HTM_TD_Begin ("class=\"LT %s_%s\"",Class.Bg,The_GetSuffix ());
	 switch (PutForm)
	   {
	    case Frm_PUT_FORM:
	       Action = Ntf_StartFormGoToAction (NotifyEvent,Hie[Hie_CRS].HieCod,UsrDat,Cod,&Forums);
	       PutForm = Frm_CheckIfInside () == Frm_INSIDE_FORM ? Frm_PUT_FORM :
								   Frm_DONT_PUT_FORM;
		  HTM_BUTTON_Submit_Begin (Txt_NOTIFY_EVENTS_SINGULAR[NotifyEvent],NULL,
					   "class=\"Ntf_TYPE LT %s_%s\"",
					   Class.Link,The_GetSuffix ());
		     HTM_Txt (Txt_NOTIFY_EVENTS_SINGULAR[NotifyEvent]);
		  HTM_BUTTON_End ();
	       Frm_EndForm ();
	       break;
	    case Frm_DONT_PUT_FORM:
	    default:
	       HTM_SPAN_Begin ("class=\"Ntf_TYPE %s\"",Class.Txt);
		  HTM_Txt (Txt_NOTIFY_EVENTS_SINGULAR[NotifyEvent]);
	       HTM_SPAN_End ();
	       break;
	   }
      HTM_TD_End ();

      /* Write user (from) */
      HTM_TD_Begin ("class=\"LT %s_%s %s_%s\"",
		    Class.Author,The_GetSuffix (),Class.Bg,The_GetSuffix ());
	 Usr_WriteAuthor (UsrDat,For_ENABLED);
      HTM_TD_End ();

      /* Write location */
      HTM_TD_Begin ("class=\"LT %s_%s\"",Class.Bg,The_GetSuffix ());
	 if (NotifyEvent == Ntf_EVENT_FORUM_POST_COURSE ||
	     NotifyEvent == Ntf_EVENT_FORUM_REPLY)
	   {
	    if (PutForm == Frm_PUT_FORM)
	      {
	       Action = Ntf_StartFormGoToAction (NotifyEvent,Hie[Hie_CRS].HieCod,UsrDat,Cod,&Forums);
	       PutForm = Frm_CheckIfInside () == Frm_INSIDE_FORM ? Frm_PUT_FORM :
								   Frm_DONT_PUT_FORM;
	      }

	    switch (PutForm)
	      {
	       case Frm_PUT_FORM:
		  HTM_BUTTON_Submit_Begin (Txt_NOTIFY_EVENTS_SINGULAR[NotifyEvent],NULL,
					   "class=\"Ntf_LOCATION %s_%s\"",
					   Class.Link,The_GetSuffix ());
		  break;
	       case Frm_DONT_PUT_FORM:
	       default:
		  HTM_DIV_Begin ("class=\"Ntf_LOCATION %s_%s\"",
				  Class.Txt,The_GetSuffix ());
		  break;
	      }
	    /* Get forum type of the post */
	    For_ResetForums (&Forums);
	    For_GetThreadForumTypeAndHieCodOfAPost (Cod,&Forums.Forum);
	    For_SetForumName (&Forums.Forum,ForumName,
			      Gbl.Prefs.Language,For_DONT_USE_HTML_ENTITIES);	// Set forum name in recipient's language
	    HTM_Txt (Txt_Forum); HTM_Colon (); HTM_SP (); HTM_Txt (ForumName);
	    switch (PutForm)
	      {
	       case Frm_PUT_FORM:
		  HTM_BUTTON_End ();
		  Frm_EndForm ();
		  break;
	       case Frm_DONT_PUT_FORM:
	       default:
		  HTM_DIV_End ();
		  break;
	      }
	   }
	 else
	   {
	    if (PutForm == Frm_PUT_FORM)
	      {
	       Action = Ntf_StartFormGoToAction (NotifyEvent,Hie[Hie_CRS].HieCod,UsrDat,Cod,&Forums);
	       PutForm = Frm_CheckIfInside () == Frm_INSIDE_FORM ? Frm_PUT_FORM :
								   Frm_DONT_PUT_FORM;
	      }

	    switch (PutForm)
	      {
	       case Frm_DONT_PUT_FORM:
		  HTM_DIV_Begin ("class=\"Ntf_LOCATION %s_%s\"",
				 Class.Txt,The_GetSuffix ());
		  break;
	       case Frm_PUT_FORM:
		  HTM_BUTTON_Submit_Begin (Txt_NOTIFY_EVENTS_SINGULAR[NotifyEvent],NULL,
					   "class=\"Ntf_LOCATION %s_%s\"",
					   Class.Link,The_GetSuffix ());
		  break;
	      }

	    for (HieLvl  = Hie_CRS;
		 HieLvl >= Hie_INS;
		 HieLvl--)
	       if (Hie[HieLvl].HieCod > 0)
		 {
	          HTM_Txt (Txt_HIERARCHY_SINGUL_Abc[HieLvl]); HTM_Colon ();
	          HTM_SP (); HTM_Txt (Hie[HieLvl].ShrtName);
		  break;
		 }
	    if (HieLvl < Hie_INS)
	       HTM_Hyphen ();

	    switch (PutForm)
	      {
	       case Frm_PUT_FORM:
		  HTM_BUTTON_End ();
		  Frm_EndForm ();
		  break;
	       case Frm_DONT_PUT_FORM:
	       default:
		  HTM_DIV_End ();
		  break;
	      }
	   }

      HTM_TD_End ();

      /* Write date and time */
      Msg_WriteMsgDate (DateTimeUTC,Class.Txt,Class.Bg);

      /* Write status (sent by email / pending to be sent by email) */
      HTM_TD_Begin ("class=\"LT %s_%s %s_%s\"",
		    Class.Txt,The_GetSuffix (),Class.Bg,The_GetSuffix ());
	 StatusTxt = Ntf_GetStatusTxtFromStatusBits (Status);
	 HTM_Txt (Txt_NOTIFICATION_STATUS[StatusTxt]);
      HTM_TD_End ();

   HTM_TR_End ();

   /***** Write content of the event *****/
   if (PutForm == Frm_PUT_FORM)
     {
      ContentStr = NULL;

      Ntf_GetNotifSummaryAndContent (SummaryStr,&ContentStr,NotifyEvent,
				     Cod,Hie[Hie_CRS].HieCod,
				     Gbl.Usrs.Me.UsrDat.UsrCod,
				     Ntf_DONT_GET_CONTENT);
      HTM_TR_Begin (NULL);

	 HTM_TD_Begin ("colspan=\"2\"");
	 HTM_TD_End ();

	 HTM_TD_Begin ("colspan=\"4\" class=\"LT DAT_%s\""
		       " style=\"padding-bottom:12px;\"",The_GetSuffix ());
	    HTM_Txt (SummaryStr);
	 HTM_TD_End ();

      HTM_TR_End ();
     }
  }

/*****************************************************************************/
/************* Get whether to show all notifications from form ***************/
/*****************************************************************************/

static bool Ntf_GetAllNotificationsFromForm (void)
  {
   return Par_GetParBool ("All");
  }

/*****************************************************************************/
/*********** Put form to go to an action depending on the event **************/
/*****************************************************************************/

static Act_Action_t Ntf_StartFormGoToAction (Ntf_NotifyEvent_t NotifyEvent,
                                             long CrsCod,const struct Usr_Data *UsrDat,long Cod,
                                             const struct For_Forums *Forums)
  {
   extern Act_Action_t For_ActionsSeeFor[For_NUM_TYPES_FORUM];
   struct Brw_FileMetadata FileMetadata;
   long InsCod = -1L;
   long CtrCod = -1L;
   long DegCod = -1L;
   long GrpCod = -1L;
   Act_Action_t Action = ActUnk;
   char *Anchor = NULL;

   /***** Parameters depending on the type of event *****/
   switch (NotifyEvent)
     {
      case Ntf_EVENT_CALL_FOR_EXAM:
	 /* Build anchor string */
	 Frm_SetAnchorStr (Cod,&Anchor);

	 /* Begin form */
	 Action = ActSeeOneCfe;
         Frm_BeginFormAnchor (ActSeeOneCfe,Anchor);
            ParCod_PutPar (ParCod_Exa,Cod);

	 /* Free anchor string */
	 Frm_FreeAnchorStr (&Anchor);
	 break;
      case Ntf_EVENT_DOCUMENT_FILE:
      case Ntf_EVENT_TEACHERS_FILE:
      case Ntf_EVENT_SHARED_FILE:
      case Ntf_EVENT_MARKS_FILE:
	 FileMetadata.FilCod = Cod;
         if (FileMetadata.FilCod > 0)
            Brw_GetFileMetadataByCod (&FileMetadata);
	 if (FileMetadata.FilCod > 0)
	   {
	    Brw_GetCrsGrpFromFileMetadata (FileMetadata.FileBrowser,FileMetadata.Cod,
					   &InsCod,&CtrCod,&DegCod,&CrsCod,&GrpCod);
	    switch (NotifyEvent)
	      {
	       case Ntf_EVENT_DOCUMENT_FILE:
		  Action = (GrpCod > 0 ? ActReqDatSeeDocGrp :
			   (CrsCod > 0 ? ActReqDatSeeDocCrs :
			   (DegCod > 0 ? ActReqDatSeeDocDeg :
			   (CtrCod > 0 ? ActReqDatSeeDocCtr :
					 ActReqDatSeeDocIns))));
		  break;
	       case Ntf_EVENT_TEACHERS_FILE:
		  Action = (GrpCod > 0 ? ActReqDatTchGrp :
					 ActReqDatTchCrs);
		  break;
	       case Ntf_EVENT_SHARED_FILE:
		  Action = (GrpCod > 0 ? ActReqDatShaGrp :
			   (CrsCod > 0 ? ActReqDatShaCrs :
			   (DegCod > 0 ? ActReqDatShaDeg :
			   (CtrCod > 0 ? ActReqDatShaCtr :
					 ActReqDatShaIns))));
		  break;
	       case Ntf_EVENT_MARKS_FILE:
		  Action = (GrpCod > 0 ? ActReqDatSeeMrkGrp :
					 ActReqDatSeeMrkCrs);
		  break;
	       default:	// Not aplicable here
		  break;
	      }
	    Frm_BeginForm (Action);
	       ParCod_PutPar (ParCod_Grp,GrpCod);
	       ParCod_PutPar (ParCod_Fil,FileMetadata.FilCod);
	   }
	 break;
      case Ntf_EVENT_TML_COMMENT:
      case Ntf_EVENT_TML_FAV:
      case Ntf_EVENT_TML_SHARE:
      case Ntf_EVENT_TML_MENTION:
	 // Cod is the code of the social publishing
	 Action = ActSeeGblTL;
         Frm_BeginForm (Action);
	    ParCod_PutPar (ParCod_Pub,Cod);
	    Usr_PutParUsrCodEncrypted (UsrDat->EnUsrCod);
	    Ntf_PutParNotifyEvent (NotifyEvent);
	 break;
      case Ntf_EVENT_FOLLOWER:
         if (UsrDat->EnUsrCod[0])	// User's code found ==>
					// go to user's public profile
           {
            Action = ActSeeOthPubPrf;
            Frm_BeginForm (Action);
	       /* Put param to go to follower's profile */
	       Usr_PutParUsrCodEncrypted (UsrDat->EnUsrCod);
           }
         else	// No user's code found ==> go to see my followers
           {
            Action = ActSeeFlr;
            Frm_BeginForm (Action);
           }
	 break;
      case Ntf_EVENT_FORUM_POST_COURSE:
      case Ntf_EVENT_FORUM_REPLY:
	 Action = For_ActionsSeeFor[Forums->Forum.Type];
	 Frm_BeginForm (Action);
	    For_PutAllParsForum (1,	// Page of threads = first
				 1,	// Page of posts   = first
				 Forums->ForumSet,
				 Forums->ThreadsOrder,
				 Forums->Forum.HieCod,
				 Forums->Thread.Selected,
				 -1L);
	 break;
      case Ntf_EVENT_NOTICE:
	 Action = ActSeeOneNot;
         Frm_BeginForm (Action);
	    ParCod_PutPar (ParCod_Not,Cod);
	 break;
      case Ntf_EVENT_MESSAGE:
	 Action = ActExpRcvMsg;
         Frm_BeginForm (Action);
	    ParCod_PutPar (ParCod_Msg,Cod);
	 break;
      default:
	 Action = Ntf_DefaultActions[NotifyEvent];
         Frm_BeginForm (Action);
	 break;
     }

   /***** Parameter to go to another course/degree/center/institution *****/
   if (Frm_CheckIfInside () == Frm_INSIDE_FORM)
     {
      if (CrsCod > 0)					// Course specified
	{
	 if (CrsCod != Gbl.Hierarchy.Node[Hie_CRS].HieCod)	// Not the current course
	    ParCod_PutPar (ParCod_Crs,CrsCod);			// Go to another course
	}
      else if (DegCod > 0)				// Degree specified
	{
	 if (DegCod != Gbl.Hierarchy.Node[Hie_DEG].HieCod)	// Not the current degree
	    ParCod_PutPar (ParCod_Deg,DegCod);			// Go to another degree
	}
      else if (CtrCod > 0)				// Center specified
	{
	 if (CtrCod != Gbl.Hierarchy.Node[Hie_CTR].HieCod)	// Not the current center
	    ParCod_PutPar (ParCod_Ctr,CtrCod);			// Go to another center
	}
      else if (InsCod > 0)				// Institution specified
	{
	 if (InsCod != Gbl.Hierarchy.Node[Hie_INS].HieCod)	// Not the current institution
	    ParCod_PutPar (ParCod_Ins,InsCod);			// Go to another institution
	}
     }

   return Action;
  }

/*****************************************************************************/
/******************* Get parameter with notify event type ********************/
/*****************************************************************************/

static void Ntf_PutParNotifyEvent (Ntf_NotifyEvent_t NotifyEvent)
  {
   Par_PutParUnsigned (NULL,"NotifyEvent",(unsigned) NotifyEvent);
  }

/*****************************************************************************/
/******************* Get parameter with notify event type ********************/
/*****************************************************************************/

Ntf_NotifyEvent_t Ntf_GetParNotifyEvent (void)
  {
   return (Ntf_NotifyEvent_t)
	  Par_GetParUnsignedLong ("NotifyEvent",
                                  0,
                                  Ntf_NUM_NOTIFY_EVENTS - 1,
                                  (unsigned long) Ntf_EVENT_UNKNOWN);
  }

/*****************************************************************************/
/****************** Set StatusTxt depending on status bits *******************/
/*****************************************************************************/
//   Ntf_STATUS_NO_EMAIL        = 0,	// --0 !(Status & Ntf_STATUS_BIT_EMAIL)
//   Ntf_STATUS_EMAIL_PENDING   = 1,	// 001  (Status & Ntf_STATUS_BIT_EMAIL) && !(Status & Ntf_STATUS_BIT_SENT) && !(Status & Ntf_STATUS_BIT_READ)
//   Ntf_STATUS_EMAIL_CANCELLED = 2,	// 101  (Status & Ntf_STATUS_BIT_EMAIL) && !(Status & Ntf_STATUS_BIT_SENT) &&  (Status & Ntf_STATUS_BIT_READ)
//   Ntf_STATUS_EMAIL_SENT      = 3,	// -11  (Status & Ntf_STATUS_BIT_EMAIL) &&  (Status & Ntf_STATUS_BIT_SENT)

Ntf_StatusTxt_t Ntf_GetStatusTxtFromStatusBits (Ntf_Status_t Status)
  {
   if (!(Status & Ntf_STATUS_BIT_EMAIL))
      return Ntf_STATUS_NO_EMAIL;
   if ( (Status & Ntf_STATUS_BIT_SENT))
      return Ntf_STATUS_EMAIL_SENT;
   if ( (Status & (Ntf_STATUS_BIT_READ | Ntf_STATUS_BIT_REMOVED)))
      return Ntf_STATUS_EMAIL_CANCELLED;
   return Ntf_STATUS_EMAIL_PENDING;
  }

/*****************************************************************************/
/******************* Get notification summary and content ********************/
/*****************************************************************************/

void Ntf_GetNotifSummaryAndContent (char SummaryStr[Ntf_MAX_BYTES_SUMMARY + 1],
                                    char **ContentStr,
                                    Ntf_NotifyEvent_t NotifyEvent,
                                    long Cod,long CrsCod,long UsrCod,
                                    Ntf_GetContent_t GetContent)
  {
   SummaryStr[0] = '\0';

   switch (NotifyEvent)
     {
      case Ntf_EVENT_UNKNOWN:
         break;
      case Ntf_EVENT_DOCUMENT_FILE:
      case Ntf_EVENT_TEACHERS_FILE:
      case Ntf_EVENT_SHARED_FILE:
	 Brw_GetSummaryAndContentOfFile (SummaryStr,ContentStr,Cod,GetContent);
         break;
      case Ntf_EVENT_ASSIGNMENT:
         Asg_GetNotifAssignment (SummaryStr,ContentStr,Cod,GetContent);
         break;
      case Ntf_EVENT_CALL_FOR_EXAM:
         Cfe_GetSummaryAndContentCallForExam (SummaryStr,ContentStr,Cod,GetContent);
         break;
      case Ntf_EVENT_MARKS_FILE:
         Mrk_GetNotifMyMarks (SummaryStr,ContentStr,Cod,UsrCod,GetContent);
         break;
      case Ntf_EVENT_ENROLMENT_STD:
      case Ntf_EVENT_ENROLMENT_NET:
      case Ntf_EVENT_ENROLMENT_TCH:
	 Enr_GetNotifEnrolment (SummaryStr,CrsCod,UsrCod);
         break;
      case Ntf_EVENT_ENROLMENT_REQUEST:
	 Enr_GetNotifEnrolmentRequest (SummaryStr,ContentStr,Cod,GetContent);
         break;
      case Ntf_EVENT_TML_COMMENT:
      case Ntf_EVENT_TML_FAV:
      case Ntf_EVENT_TML_SHARE:
      case Ntf_EVENT_TML_MENTION:
	 // Cod is the code of the social publishing
	 TmlNtf_GetNotifPublication (SummaryStr,ContentStr,Cod,GetContent);
         break;
      case Ntf_EVENT_FOLLOWER:
         Fol_GetNotifFollower (SummaryStr,ContentStr);
         break;
      case Ntf_EVENT_FORUM_POST_COURSE:
      case Ntf_EVENT_FORUM_REPLY:
         For_GetSummaryAndContentForumPst (SummaryStr,ContentStr,Cod,GetContent);
         break;
      case Ntf_EVENT_NOTICE:
         Not_GetSummaryAndContentNotice (SummaryStr,ContentStr,Cod,GetContent);
         break;
      case Ntf_EVENT_MESSAGE:
         Msg_GetNotifMessage (SummaryStr,ContentStr,Cod,GetContent);
         if (Gbl.WebService.IsWebService)
            /* Set the message as open by me, because I can read it in an extern application */
            Msg_DB_SetRcvMsgAsOpen (Cod,UsrCod);
         break;
      case Ntf_EVENT_SURVEY:
         Svy_GetNotifSurvey (SummaryStr,ContentStr,Cod,GetContent);
         break;
     }
  }

/*****************************************************************************/
/*********** Mark possible notifications of one file as removed **************/
/*****************************************************************************/

void Ntf_MarkNotifOneFileAsRemoved (const char *Path)
  {
   extern const Brw_FileBrowser_t Brw_DB_FileBrowserForDB_files[Brw_NUM_TYPES_FILE_BROWSER];
   static Ntf_NotifyEvent_t NotifyEvent[Brw_NUM_TYPES_FILE_BROWSER] =
     {
      [Brw_ADMI_DOC_CRS] = Ntf_EVENT_DOCUMENT_FILE,
      [Brw_ADMI_DOC_GRP] = Ntf_EVENT_DOCUMENT_FILE,
      [Brw_ADMI_TCH_CRS] = Ntf_EVENT_TEACHERS_FILE,
      [Brw_ADMI_TCH_GRP] = Ntf_EVENT_TEACHERS_FILE,
      [Brw_ADMI_SHR_CRS] = Ntf_EVENT_SHARED_FILE,
      [Brw_ADMI_SHR_GRP] = Ntf_EVENT_SHARED_FILE,
      [Brw_ADMI_MRK_CRS] = Ntf_EVENT_MARKS_FILE,
      [Brw_ADMI_MRK_GRP] = Ntf_EVENT_MARKS_FILE,
     };
   Brw_FileBrowser_t FileBrowser = Brw_DB_FileBrowserForDB_files[Gbl.FileBrowser.Type];
   long FilCod;

   if (NotifyEvent[FileBrowser])
     {
      /***** Get file code *****/
      FilCod = Brw_DB_GetFilCodByPath (Path,Brw_ANY_FILE);	// Any file, public or not
      if (FilCod > 0)
	 /***** Set notification as removed *****/
	 Ntf_DB_MarkNotifAsRemoved (NotifyEvent[FileBrowser],FilCod);
     }
  }

/*****************************************************************************/
/*** Mark possible notifications involving children of a folder as removed ***/
/*****************************************************************************/

void Ntf_MarkNotifChildrenOfFolderAsRemoved (const char *Path)
  {
   extern const Brw_FileBrowser_t Brw_DB_FileBrowserForDB_files[Brw_NUM_TYPES_FILE_BROWSER];
   static Ntf_NotifyEvent_t NotifyEvent[Brw_NUM_TYPES_FILE_BROWSER] =
     {
      [Brw_ADMI_DOC_CRS] = Ntf_EVENT_DOCUMENT_FILE,
      [Brw_ADMI_DOC_GRP] = Ntf_EVENT_DOCUMENT_FILE,
      [Brw_ADMI_TCH_CRS] = Ntf_EVENT_TEACHERS_FILE,
      [Brw_ADMI_TCH_GRP] = Ntf_EVENT_TEACHERS_FILE,
      [Brw_ADMI_SHR_CRS] = Ntf_EVENT_SHARED_FILE,
      [Brw_ADMI_SHR_GRP] = Ntf_EVENT_SHARED_FILE,
      [Brw_ADMI_MRK_CRS] = Ntf_EVENT_MARKS_FILE,
      [Brw_ADMI_MRK_GRP] = Ntf_EVENT_MARKS_FILE,
     };
   Brw_FileBrowser_t FileBrowser = Brw_DB_FileBrowserForDB_files[Gbl.FileBrowser.Type];

   if (NotifyEvent[FileBrowser])
      /***** Set notification as removed *****/
      Ntf_DB_MarkNotifChildrenOfFolderAsRemoved (NotifyEvent[FileBrowser],FileBrowser,
						 Brw_GetCodForFileBrowser (Gbl.FileBrowser.Type),
						 Path);
  }

/*****************************************************************************/
/********** Get a list with user's codes of all users to be notified *********/
/********** about an event, and notify them                          *********/
/*****************************************************************************/
// Return the number of users notified by email

unsigned Ntf_StoreNotifyEventsToAllUsrs (Ntf_NotifyEvent_t NotifyEvent,long Cod)
  {
   extern bool Brw_TypeIsGrpBrw[Brw_NUM_TYPES_FILE_BROWSER];
   static unsigned (*GetUsrsForum[For_NUM_TYPES_FORUM]) (MYSQL_RES **mysql_res,long CrsGrpCod) =
     {
      [For_FORUM_COURSE_USRS] = Enr_DB_GetUsrsFromCrsExceptMe,
      [For_FORUM_COURSE_TCHS] = Enr_DB_GetTchsFromCrsExceptMe,
     };
   MYSQL_RES *mysql_res;
   unsigned NumUsrs = 0;	// Initialized to avoid warning
   unsigned NumUsr;
   struct Usr_Data UsrDat;
   struct For_Forum ForumSelected;
   long InsCod;
   long CtrCod;
   long DegCod;
   long CrsCod;
   unsigned NumUsrsToBeNotifiedByEMail = 0;
   unsigned NotifyEventMask = (1 << NotifyEvent);

   /***** Get users who want to be notified from database ******/
   switch (NotifyEvent)
     {
      case Ntf_EVENT_UNKNOWN:	// This function should not be called in this case
         return 0;
      case Ntf_EVENT_DOCUMENT_FILE:
      case Ntf_EVENT_SHARED_FILE:
      case Ntf_EVENT_MARKS_FILE:
	 NumUsrs = Brw_TypeIsGrpBrw[Gbl.FileBrowser.Type] ? Grp_DB_GetUsrsFromGrpExceptMe (&mysql_res,Brw_GetGrpCod ()) :
							    Enr_DB_GetUsrsFromCrsExceptMe (&mysql_res,Gbl.Hierarchy.Node[Hie_CRS].HieCod);
	 break;
      case Ntf_EVENT_TEACHERS_FILE:
	 NumUsrs = Brw_TypeIsGrpBrw[Gbl.FileBrowser.Type] ? Grp_DB_GetTchsFromGrpExceptMe (&mysql_res,Brw_GetGrpCod ()) :
							    Enr_DB_GetTchsFromCrsExceptMe (&mysql_res,Gbl.Hierarchy.Node[Hie_CRS].HieCod);
	 break;
      case Ntf_EVENT_ASSIGNMENT:
         NumUsrs = Asg_DB_GetUsrsFromAssignmentExceptMe (&mysql_res,Cod);
         break;
      case Ntf_EVENT_CALL_FOR_EXAM:
      case Ntf_EVENT_NOTICE:
         NumUsrs = Enr_DB_GetUsrsFromCrsExceptMe (&mysql_res,Gbl.Hierarchy.Node[Hie_CRS].HieCod);
         break;
      case Ntf_EVENT_ENROLMENT_STD:	// This function should not be called in this case
      case Ntf_EVENT_ENROLMENT_NET:	// This function should not be called in this case
      case Ntf_EVENT_ENROLMENT_TCH:	// This function should not be called in this case
         return 0;
      case Ntf_EVENT_ENROLMENT_REQUEST:
	 if (Enr_GetNumUsrsInCrss (Hie_CRS,Gbl.Hierarchy.Node[Hie_CRS].HieCod,1 << Rol_TCH))
	    // If this course has teachers ==> send notification to teachers
            NumUsrs = Enr_DB_GetTchsFromCrsExceptMe (&mysql_res,Gbl.Hierarchy.Node[Hie_CRS].HieCod);
	 else	// Course without teachers
	    // If this course has no teachers
	    // and I want to be a teacher (checked before calling this function
	    // to not send requests to be a student to admins)
	    // ==> send notification to administrators or superusers
	    NumUsrs = Adm_DB_GetAdmsCurrentScopeExceptMe (&mysql_res);
         break;
      case Ntf_EVENT_TML_COMMENT:	// New comment to one of my social notes or comments
         // Cod is the code of the social publishing
	 NumUsrs = Tml_DB_GetPublishersInNoteExceptMe (&mysql_res,Cod);
         break;
      case Ntf_EVENT_TML_FAV:		// New favourite to one of my social notes or comments
      case Ntf_EVENT_TML_SHARE:		// New sharing of one of my social notes
      case Ntf_EVENT_TML_MENTION:
      case Ntf_EVENT_FOLLOWER:
	 // This function should not be called in these cases
         return 0;
      case Ntf_EVENT_FORUM_POST_COURSE:
	 // Check if forum is for users or for all users in the course
	 For_GetThreadForumTypeAndHieCodOfAPost (Cod,&ForumSelected);
	 if (GetUsrsForum[ForumSelected.Type])
            NumUsrs = GetUsrsForum[ForumSelected.Type] (&mysql_res,Gbl.Hierarchy.Node[Hie_CRS].HieCod);
	 else
	    return 0;
         break;
      case Ntf_EVENT_FORUM_REPLY:
         // Cod is the code of the post
         NumUsrs = For_DB_GetPublishersInThreadExceptMe (&mysql_res,Cod);
         break;
      case Ntf_EVENT_MESSAGE:		// This function should not be called in this case
	 return 0;
      case Ntf_EVENT_SURVEY:	// Only surveys for a course are notified, not surveys for a degree or global
         NumUsrs = Svy_DB_GetUsrsFromSurveyExceptMe (&mysql_res,Cod);
         break;
     }

   if (NotifyEvent == Ntf_EVENT_FORUM_POST_COURSE ||
       NotifyEvent == Ntf_EVENT_FORUM_REPLY)
     {
      InsCod = CtrCod = DegCod = CrsCod = -1L;
      switch (ForumSelected.Type)
        {
	 case For_FORUM_INSTIT_USRS:
	 case For_FORUM_INSTIT_TCHS:
            InsCod = ForumSelected.HieCod;
            break;
	 case For_FORUM_CENTER_USRS:
	 case For_FORUM_CENTER_TCHS:
            CtrCod = ForumSelected.HieCod;
            break;
	 case For_FORUM_DEGREE_USRS:
	 case For_FORUM_DEGREE_TCHS:
            DegCod = ForumSelected.HieCod;
            break;
	 case For_FORUM_COURSE_USRS:
	 case For_FORUM_COURSE_TCHS:
            CrsCod = ForumSelected.HieCod;
            break;
	 default:
	    break;
        }
     }
   else
     {
      InsCod = Gbl.Hierarchy.Node[Hie_INS].HieCod;
      CtrCod = Gbl.Hierarchy.Node[Hie_CTR].HieCod;
      DegCod = Gbl.Hierarchy.Node[Hie_DEG].HieCod;
      CrsCod = Gbl.Hierarchy.Node[Hie_CRS].HieCod;
     }

   if (NumUsrs) // Users found
     {
      /***** Initialize structure with user's data *****/
      Usr_UsrDataConstructor (&UsrDat);

      /***** Notify the users one by one *****/
      for (NumUsr = 0;
	   NumUsr < NumUsrs;
	   NumUsr++)
	{
         /* Get next user */
         UsrDat.UsrCod = DB_GetNextCode (mysql_res);

	 if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&UsrDat,	// Get user's data from database
	                                              Usr_DONT_GET_PREFS,
	                                              Usr_DONT_GET_ROLE_IN_CRS) == Exi_EXISTS)
            if ((UsrDat.NtfEvents.CreateNotif & NotifyEventMask))	// Create notification
              {
	       if ((UsrDat.NtfEvents.SendEmail & NotifyEventMask))	// Send notification by email
		 {
		  Ntf_DB_StoreNotifyEventToUsr (NotifyEvent,UsrDat.UsrCod,Cod,
						(Ntf_Status_t) Ntf_STATUS_BIT_EMAIL,
						InsCod,CtrCod,DegCod,CrsCod);
		  NumUsrsToBeNotifiedByEMail++;
		 }
	       else							// Don't send notification by email
		  Ntf_DB_StoreNotifyEventToUsr (NotifyEvent,UsrDat.UsrCod,Cod,
		                                (Ntf_Status_t) 0,
						InsCod,CtrCod,DegCod,CrsCod);
              }
        }

      /***** Free memory used for user's data *****/
      Usr_UsrDataDestructor (&UsrDat);
     }

   /***** Free structure that stores the query result *****/
   DB_FreeMySQLResult (&mysql_res);

   return NumUsrsToBeNotifiedByEMail;
  }

/*****************************************************************************/
/***************** Send all pending notifications by email *******************/
/*****************************************************************************/

void Ntf_SendPendingNotifByEMailToAllUsrs (void)
  {
   MYSQL_RES *mysql_res;
   unsigned NumUsrs;
   unsigned NumUsr;
   struct Usr_Data ToUsrDat;
   unsigned NumNotif;
   unsigned NumTotalNotif = 0;
   unsigned NumMails;
   unsigned NumTotalMails = 0;

   /***** Get users who must be notified from database ******/
   if ((NumUsrs = Ntf_DB_GetUsrsWhoMustBeNotified (&mysql_res)))
     {
      /***** Initialize structure with user's data *****/
      Usr_UsrDataConstructor (&ToUsrDat);

      /***** Notify the users one by one *****/
      for (NumUsr = 0;
	   NumUsr < NumUsrs;
	   NumUsr++)
	{
         /* Get next user */
         ToUsrDat.UsrCod = DB_GetNextCode (mysql_res);

         /* Get user's data */
	 if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&ToUsrDat,	// Get user's data from database
	                                              Usr_GET_PREFS,	// User's language necessary to write email
	                                              Usr_DONT_GET_ROLE_IN_CRS) == Exi_EXISTS)
           {
            /* Send one email to this user */
            Ntf_SendPendingNotifByEMailToOneUsr (&ToUsrDat,&NumNotif,&NumMails);
            NumTotalNotif += NumNotif;
            NumTotalMails += NumMails;
           }
        }

      /***** Free memory used for user's data *****/
      Usr_UsrDataDestructor (&ToUsrDat);
     }

   /***** Free structure that stores the query result *****/
   DB_FreeMySQLResult (&mysql_res);

   /***** Delete old notifications ******/
   Ntf_DB_RemoveOldNtfs ();
  }

/*****************************************************************************/
/************ Send pending notifications of one user by email ****************/
/*****************************************************************************/

static void Ntf_SendPendingNotifByEMailToOneUsr (const struct Usr_Data *ToUsrDat,
                                                 unsigned *NumNotif,
                                                 unsigned *NumMails)
  {
   extern Err_SuccessOrError_t (*Hie_GetDataByCod[Hie_NUM_LEVELS]) (struct Hie_Node *Node);
   extern const char *Txt_NOTIFY_EVENTS_There_is_a_new_event_NO_HTML[1 + Lan_NUM_LANGUAGES];
   extern const char *Txt_NOTIFY_EVENTS_There_are_X_new_events_NO_HTML[1 + Lan_NUM_LANGUAGES];
   extern const char *Txt_NOTIFY_EVENTS_SINGULAR_NO_HTML[Ntf_NUM_NOTIFY_EVENTS][1 + Lan_NUM_LANGUAGES];
   extern const char *Txt_Course_NO_HTML[1 + Lan_NUM_LANGUAGES];
   extern const char *Txt_Forum_NO_HTML[1 + Lan_NUM_LANGUAGES];
   extern const char *Txt_MSG_From_NO_HTML[1 + Lan_NUM_LANGUAGES];
   extern const char *Txt_Go_to_NO_HTML[1 + Lan_NUM_LANGUAGES];
   extern const char *Txt_TAB_Messages_NO_HTML[1 + Lan_NUM_LANGUAGES];
   extern const char *Txt_Notifications_NO_HTML[1 + Lan_NUM_LANGUAGES];
   extern const char *Txt_If_you_no_longer_wish_to_receive_email_notifications_NO_HTML[1 + Lan_NUM_LANGUAGES];
   MYSQL_RES *mysql_res;
   MYSQL_ROW row;
   unsigned NumNtfs;
   unsigned NumNtf;
   Lan_Language_t ToUsrLanguage;
   Ntf_NotifyEvent_t NotifyEvent = (Ntf_NotifyEvent_t) 0;	// Initialized to avoid warning
   struct Usr_Data FromUsrDat;
   __attribute__((unused)) Exi_Exist_t UsrExists;
   Hie_Level_t HieLvl;
   struct Hie_Node Hie[Hie_NUM_LEVELS];
   __attribute__((unused)) Err_SuccessOrError_t SuccessOrError;
   unsigned Col;
   long Cod;
   struct For_Forum ForumSelected;
   char ForumName[For_MAX_BYTES_FORUM_NAME + 1];
   char FileNameMail[PATH_MAX + 1];
   FILE *FileMail;
   int ReturnCode;

   /***** Return 0 notifications and 0 mails when error *****/
   *NumNotif =
   *NumMails = 0;

   if (Mai_CheckIfUsrCanReceiveEmailNotif (ToUsrDat) == Usr_CAN)
     {
      /***** Get pending notifications of this user from database ******/
      if ((NumNtfs = Ntf_DB_GetPendingNtfsToUsr (&mysql_res,ToUsrDat->UsrCod))) // Notifications found
	{
	 /***** If user has no language, set it to current language *****/
	 ToUsrLanguage = ToUsrDat->Prefs.Language;
	 if (ToUsrLanguage == Lan_LANGUAGE_UNKNOWN)
	    ToUsrLanguage = Cfg_DEFAULT_LANGUAGE;

	 /***** Create temporary file for mail content *****/
	 Mai_CreateFileNameMail (FileNameMail,&FileMail);

	 /***** Welcome note *****/
	 Mai_WriteWelcomeNoteEMail (FileMail,ToUsrDat,ToUsrLanguage);
	 if (NumNtfs == 1)
	    fprintf (FileMail,Txt_NOTIFY_EVENTS_There_is_a_new_event_NO_HTML[ToUsrLanguage],
		     Cfg_PLATFORM_SHORT_NAME);
	 else
	    fprintf (FileMail,Txt_NOTIFY_EVENTS_There_are_X_new_events_NO_HTML[ToUsrLanguage],
		     (unsigned) NumNtfs,Cfg_PLATFORM_SHORT_NAME);
	 fprintf (FileMail,": \n");

	 /***** Initialize structure with origin user's data *****/
	 Usr_UsrDataConstructor (&FromUsrDat);

	 /***** Inform about the events one by one *****/
	 for (NumNtf = 0;
	      NumNtf < NumNtfs;
	      NumNtf++)
	   {
	    /* Get next event */
	    row = mysql_fetch_row (mysql_res);
	    /*
	    row[0]: NotifyEvent
	    row[1]: FromUsrCod
	    row[2]: InsCod
	    row[3]: CtrCod
	    row[4]: DegCod
	    row[5]: CrsCod
	    row[6]: Cod
	    */
	    /* Get event type (row[0]) */
	    NotifyEvent = Ntf_GetNotifyEventFromStr (row[0]);

	    /* Get origin user code (row[1]) */
	    FromUsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[1]);
	    UsrExists = Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&FromUsrDat,	// Get origin user's data from database
								 Usr_DONT_GET_PREFS,
								 Usr_DONT_GET_ROLE_IN_CRS);

	    /* Get data of institution, center, degree and course
	       (row[2], row[3], row[4], row[5]) */
	    for (HieLvl  = Hie_INS, Col = 2;
		 HieLvl <= Hie_CRS;
		 HieLvl++, Col++)
	       {
	        Hie[HieLvl].HieCod = Str_ConvertStrCodToLongCod (row[Col]);
		SuccessOrError = Hie_GetDataByCod[HieLvl] (&Hie[HieLvl]);
	       }

	    /* Get message/post/... code (row[6]) */
	    Cod = Str_ConvertStrCodToLongCod (row[6]);

	    /* Get forum type */
	    if (NotifyEvent == Ntf_EVENT_FORUM_POST_COURSE ||
		NotifyEvent == Ntf_EVENT_FORUM_REPLY)
	       For_GetThreadForumTypeAndHieCodOfAPost (Cod,&ForumSelected);

	    /* Information about the type of this event */
	    fprintf (FileMail,Txt_NOTIFY_EVENTS_SINGULAR_NO_HTML[NotifyEvent][ToUsrLanguage],
		     Cfg_PLATFORM_SHORT_NAME);
	    fprintf (FileMail,"\n");

	    /* Course/forum: */
	    switch (NotifyEvent)
	      {
	       case Ntf_EVENT_UNKNOWN:
	       case Ntf_EVENT_TML_COMMENT:
	       case Ntf_EVENT_TML_FAV:
	       case Ntf_EVENT_TML_SHARE:
	       case Ntf_EVENT_TML_MENTION:
	       case Ntf_EVENT_FOLLOWER:
		  break;
	       case Ntf_EVENT_DOCUMENT_FILE:
	       case Ntf_EVENT_TEACHERS_FILE:
	       case Ntf_EVENT_SHARED_FILE:
	       case Ntf_EVENT_ASSIGNMENT:
	       case Ntf_EVENT_CALL_FOR_EXAM:
	       case Ntf_EVENT_MARKS_FILE:
	       case Ntf_EVENT_ENROLMENT_STD:
	       case Ntf_EVENT_ENROLMENT_NET:
	       case Ntf_EVENT_ENROLMENT_TCH:
	       case Ntf_EVENT_ENROLMENT_REQUEST:
	       case Ntf_EVENT_NOTICE:
	       case Ntf_EVENT_MESSAGE:
	       case Ntf_EVENT_SURVEY:
		  if (Hie[Hie_CRS].HieCod > 0)
		     fprintf (FileMail,"%s: %s\n",
			      Txt_Course_NO_HTML[ToUsrLanguage],
			      Hie[Hie_CRS].FullName);
		  break;
	       case Ntf_EVENT_FORUM_POST_COURSE:
	       case Ntf_EVENT_FORUM_REPLY:
		  For_SetForumName (&ForumSelected,ForumName,
				    ToUsrLanguage,For_DONT_USE_HTML_ENTITIES);	// Set forum name in recipient's language
		  fprintf (FileMail,"%s: %s\n",
			   Txt_Forum_NO_HTML[ToUsrLanguage],
			   ForumName);
		  break;
	      }
	    /* From: */
	    fprintf (FileMail,"%s: %s\n",
		     Txt_MSG_From_NO_HTML[ToUsrLanguage],
		     FromUsrDat.FullName);
	   }

	 /***** Free memory used for origin user's data *****/
	 Usr_UsrDataDestructor (&FromUsrDat);

	 /* Go to: */
	 fprintf (FileMail,"%s: %s/ > %s > %s\n",
		  Txt_Go_to_NO_HTML[ToUsrLanguage],
		  Cfg_URL_SWAD_CGI,
		  Txt_TAB_Messages_NO_HTML[ToUsrLanguage],
		  Txt_Notifications_NO_HTML[ToUsrLanguage]);

	 /* Disclaimer */
	 fprintf (FileMail,"\n%s\n",
		  Txt_If_you_no_longer_wish_to_receive_email_notifications_NO_HTML[ToUsrLanguage]);

	 /* Footer note */
	 Mai_WriteFootNoteEMail (FileMail,ToUsrLanguage);

	 fclose (FileMail);

	 /***** Call the command to send an email *****/
         ReturnCode = Mai_SendMailMsg (FileNameMail,
                                       Txt_Notifications_NO_HTML[ToUsrLanguage],
                                       ToUsrDat->Email);

	 /***** Remove temporary file *****/
	 unlink (FileNameMail);

	 /***** Update number of notifications, number of mails and statistics *****/
	 if (ReturnCode == 0)	// Message sent successfully
	   {
	    *NumNotif = NumNtfs;
	    *NumMails = 1;

	    /* Update statistics about notifications */
	    Ntf_UpdateNumNotifSent (Hie[Hie_DEG].HieCod,Hie[Hie_CRS].HieCod,NotifyEvent,
	                            *NumNotif,*NumMails);
	   }

	 /***** Mark all pending notifications of this user as 'sent' *****/
	 Ntf_DB_MarkPendingNtfsAsSent (ToUsrDat->UsrCod);
	}

      /***** Free structure that stores the query result *****/
      DB_FreeMySQLResult (&mysql_res);
     }
  }

/*****************************************************************************/
/****** Get notify event type from string number coming from database ********/
/*****************************************************************************/

Ntf_NotifyEvent_t Ntf_GetNotifyEventFromStr (const char *Str)
  {
   unsigned UnsignedNum;

   if (sscanf (Str,"%u",&UnsignedNum) == 1)
      if (UnsignedNum < Ntf_NUM_NOTIFY_EVENTS)
         return (Ntf_NotifyEvent_t) UnsignedNum;

   return Ntf_EVENT_UNKNOWN;
  }

/*****************************************************************************/
/************* Get number of events notified and emails sent *****************/
/*****************************************************************************/

static void Ntf_GetNumNotifSent (long DegCod,long CrsCod,
                                 Ntf_NotifyEvent_t NotifyEvent,
                                 unsigned *NumEvents,unsigned *NumMails)
  {
   MYSQL_RES *mysql_res;
   MYSQL_ROW row;

   /***** Get number of notifications sent by email from database *****/
   if (Ntf_DB_GetNumNotifSent (&mysql_res,DegCod,CrsCod,NotifyEvent))
     {
      row = mysql_fetch_row (mysql_res);
      if (sscanf (row[0],"%u",NumEvents) != 1)
         Err_ShowErrorAndExit ("Error when getting number of notifications sent by email.");
      if (sscanf (row[1],"%u",NumMails) != 1)
         Err_ShowErrorAndExit ("Error when getting number of notifications sent by email.");
     }
   else
      *NumEvents = *NumMails = 0;

   /***** Free structure that stores the query result *****/
   DB_FreeMySQLResult (&mysql_res);
  }

/*****************************************************************************/
/********** Update number of notifications and notify emails sent ************/
/*****************************************************************************/

static void Ntf_UpdateNumNotifSent (long DegCod,long CrsCod,
                                    Ntf_NotifyEvent_t NotifyEvent,
                                    unsigned NumEvents,unsigned NumMails)
  {
   unsigned CurrentNumEvents;
   unsigned CurrentNumMails;

   /***** Get number of events notified and number of mails sent *****/
   Ntf_GetNumNotifSent (DegCod,CrsCod,NotifyEvent,&CurrentNumEvents,&CurrentNumMails);

   /***** Update number of notifications and notify emails sent *****/
   Ntf_DB_UpdateNumNotifSent (DegCod,CrsCod,NotifyEvent,
                              CurrentNumEvents + NumEvents,
                              CurrentNumMails + NumMails);
  }

/*****************************************************************************/
/******************** Mark all my notifications as seen **********************/
/*****************************************************************************/

void Ntf_MarkAllNotifAsSeen (void)
  {
   /***** Set all my notifications as seen *****/
   Ntf_DB_MarkAllMyNotifAsSeen ();

   /***** Show my notifications again *****/
   Ntf_ShowMyNotifications ();
  }

/*****************************************************************************/
/*** Activate the sending of email to notify me that I have new messages *****/
/*****************************************************************************/

void Ntf_PutFormChangeNotifSentByEMail (void)
  {
   extern const char *Hlp_PROFILE_Settings_notifications;
   extern const char *Txt_Notifications;
   extern const char *Txt_Create_BR_notification;
   extern const char *Txt_Notify_me_BR_by_email;
   extern const char *Txt_NOTIFY_EVENTS_PLURAL[Ntf_NUM_NOTIFY_EVENTS];
   Ntf_NotifyEvent_t NotifyEvent;

   /***** Begin section with settings on privacy *****/
   HTM_SECTION_Begin (Ntf_NOTIFICATIONS_ID);

      /***** Begin box *****/
      Box_BoxBegin (Txt_Notifications,Ntf_PutIconsNotif,NULL,
		    Hlp_PROFILE_Settings_notifications,Box_NOT_CLOSABLE);

	 /***** Begin form *****/
	 Frm_BeginForm (ActChgNtfPrf);

	    /***** Warning if I can not receive email notifications *****/
	    if (Mai_CheckIfUsrCanReceiveEmailNotif (&Gbl.Usrs.Me.UsrDat) == Usr_CAN_NOT)
	       Mai_WriteWarningEmailNotifications ();

	    /***** List of notifications *****/
	    HTM_TABLE_BeginCenterPadding (2);

	       HTM_TR_Begin (NULL);
		  HTM_TH_Empty (1);
		  HTM_TH (Txt_Create_BR_notification,HTM_HEAD_CENTER);
		  HTM_TH (Txt_Notify_me_BR_by_email ,HTM_HEAD_CENTER);
	       HTM_TR_End ();

	       /***** Checkbox to activate internal notifications and email notifications
		      about events *****/
	       for (NotifyEvent  = (Ntf_NotifyEvent_t) 1;
		    NotifyEvent <= (Ntf_NotifyEvent_t) (Ntf_NUM_NOTIFY_EVENTS - 1);
		    NotifyEvent++)	// O is reserved for Ntf_EVENT_UNKNOWN
		 {
		  HTM_TR_Begin (NULL);

		     HTM_TD_Begin ("class=\"RM FORM_IN_%s\"",The_GetSuffix ());
			HTM_Txt (Txt_NOTIFY_EVENTS_PLURAL[NotifyEvent]);
			HTM_Colon ();
		     HTM_TD_End ();

		     HTM_TD_Begin ("class=\"CM\"");
			HTM_INPUT_CHECKBOX (Ntf_ParNotifMeAboutNotifyEvents[NotifyEvent],
					    (Gbl.Usrs.Me.UsrDat.NtfEvents.CreateNotif &
					     (1 << NotifyEvent)) ? HTM_CHECKED :
								   HTM_NO_ATTR,
					    "value=\"Y\"");
		     HTM_TD_End ();

		     HTM_TD_Begin ("class=\"CM\"");
			HTM_INPUT_CHECKBOX (Ntf_ParEmailMeAboutNotifyEvents[NotifyEvent],
					    (Gbl.Usrs.Me.UsrDat.NtfEvents.SendEmail &
				             (1 << NotifyEvent)) ? HTM_CHECKED :
				        			   HTM_NO_ATTR,
					    "value=\"Y\"");
		     HTM_TD_End ();

		  HTM_TR_End ();
		 }

	    HTM_TABLE_End ();

	    /***** Button to save changes *****/
	    Btn_PutButton (Btn_SAVE_CHANGES,NULL);

	 /***** End form *****/
	 Frm_EndForm ();

      /***** End box *****/
      Box_BoxEnd ();

   /***** End section with settings about notifications *****/
   HTM_SECTION_End ();
  }

/*****************************************************************************/
/** Get parameter with the sending of email to notify me that I have msgs. ***/
/*****************************************************************************/

static void Ntf_GetParsNotifyEvents (void)
  {
   Ntf_NotifyEvent_t NotifyEvent;
   bool CreateNotifForThisEvent;

   Gbl.Usrs.Me.UsrDat.NtfEvents.CreateNotif = 0;
   Gbl.Usrs.Me.UsrDat.NtfEvents.SendEmail = 0;
   for (NotifyEvent  = (Ntf_NotifyEvent_t) 1;
	NotifyEvent <= (Ntf_NotifyEvent_t) (Ntf_NUM_NOTIFY_EVENTS - 1);
	NotifyEvent++)	// 0 is reserved for Ntf_EVENT_UNKNOWN
     {
      if ((CreateNotifForThisEvent = Par_GetParBool (Ntf_ParNotifMeAboutNotifyEvents[NotifyEvent])))
         Gbl.Usrs.Me.UsrDat.NtfEvents.CreateNotif |= (1 << NotifyEvent);

      if (CreateNotifForThisEvent)
	{
         Par_GetParBool (Ntf_ParEmailMeAboutNotifyEvents[NotifyEvent]);
         if (Par_GetParBool (Ntf_ParEmailMeAboutNotifyEvents[NotifyEvent]))
            Gbl.Usrs.Me.UsrDat.NtfEvents.SendEmail |= (1 << NotifyEvent);
	}
     }
  }

/*****************************************************************************/
/******* Change my setting about sending me notify emails about events *******/
/*****************************************************************************/

void Ntf_ChangeNotifyEvents (void)
  {
   extern const char *Txt_Your_settings_about_notifications_have_changed;

   /***** Get param with whether notify me about events *****/
   Ntf_GetParsNotifyEvents ();

   /***** Store settings about notify events *****/
   Set_DB_UpdateMySettingsAboutNotifyEvents ();

   /***** Show message *****/
   Ale_ShowAlert (Ale_SUCCESS,Txt_Your_settings_about_notifications_have_changed);
  }

/*****************************************************************************/
/************************ Write number of notifications **********************/
/*****************************************************************************/

void Ntf_WriteNumberOfNewNtfs (void)
  {
   extern const char *Txt_notification;
   extern const char *Txt_notifications;
   extern const char *Txt_Notifications;
   extern const char *Txt_NOTIF_new_SINGULAR;
   extern const char *Txt_NOTIF_new_PLURAL;
   unsigned NumUnseenNtfs;
   unsigned NumNewNtfs = 0;

   /***** Get my number of unseen notifications *****/
   if ((NumUnseenNtfs = Ntf_DB_GetNumAllMyUnseenNtfs ()))
      NumNewNtfs = Ntf_DB_GetNumMyNewUnseenNtfs ();

   /***** Begin form *****/
   Frm_BeginForm (ActSeeNewNtf);

      /***** Begin link *****/
      HTM_BUTTON_Submit_Begin (Act_GetActionText (ActSeeNewNtf),NULL,
			       "class=\"BT_LINK\"");

	 /***** Number of unseen notifications *****/
	 HTM_SPAN_Begin ("id=\"notif_all\"");
	    HTM_UnsignedTxt (NumUnseenNtfs,Txt_notification,Txt_notifications);
	 HTM_SPAN_End ();

	 /***** Icon and number of new notifications *****/
	 if (NumNewNtfs)
	   {
	    HTM_BR ();
	    HTM_IMG (Cfg_URL_ICON_PUBLIC,"bell.svg",Txt_Notifications,
		     "class=\"ICO16x16 NOTIF_ICO_%s\"",The_GetSuffix ());
	    HTM_NBSP ();
	    HTM_SPAN_Begin ("id=\"notif_new\"");
	       HTM_UnsignedTxt (NumNewNtfs,
				Txt_NOTIF_new_SINGULAR,Txt_NOTIF_new_PLURAL);
	    HTM_SPAN_End ();
	   }

      /***** End link *****/
      HTM_BUTTON_End ();

   /***** End form *****/
   Frm_EndForm ();
  }

/*****************************************************************************/
/****** Get and show number of users who want to be notified by email ********/
/*****************************************************************************/

void Ntf_GetAndShowNumUsrsPerNotifyEvent (Hie_Level_t HieLvl)
  {
   extern const char *Hlp_ANALYTICS_Figures_notifications;
   extern const char *Txt_FIGURE_TYPES[Fig_NUM_FIGURES];
   extern const char *Txt_Event;
   extern const char *Txt_NOTIFY_EVENTS_PLURAL[Ntf_NUM_NOTIFY_EVENTS];
   extern const char *Txt_Number_of_users;
   extern const char *Txt_PERCENT_of_users;
   extern const char *Txt_Number_of_events;
   extern const char *Txt_Number_of_emails;
   extern const char *Txt_Total;
   Ntf_NotifyEvent_t NotifyEvent;
   char *SubQuery;
   MYSQL_RES *mysql_res;
   MYSQL_ROW row;
   unsigned NumUsrsTotal;
   unsigned NumUsrsTotalWhoWantToBeNotifiedByEMailAboutSomeEvent;
   unsigned NumUsrs[Ntf_NUM_NOTIFY_EVENTS];
   unsigned NumEventsTotal = 0;
   unsigned NumEvents[Ntf_NUM_NOTIFY_EVENTS];
   unsigned NumMailsTotal = 0;
   unsigned NumMails[Ntf_NUM_NOTIFY_EVENTS];

   /***** Begin box and table *****/
   Box_BoxTableBegin (Txt_FIGURE_TYPES[Fig_NOTIFY_EVENTS],NULL,NULL,
                      Hlp_ANALYTICS_Figures_notifications,Box_NOT_CLOSABLE,2);

      /***** Heading row *****/
      HTM_TR_Begin (NULL);
	 HTM_TH (Txt_Event           ,HTM_HEAD_LEFT);
	 HTM_TH (Txt_Number_of_users ,HTM_HEAD_RIGHT);
	 HTM_TH (Txt_PERCENT_of_users,HTM_HEAD_RIGHT);
	 HTM_TH (Txt_Number_of_events,HTM_HEAD_RIGHT);
	 HTM_TH (Txt_Number_of_emails,HTM_HEAD_RIGHT);
      HTM_TR_End ();

      /***** Get total number of users *****/
      NumUsrsTotal = Usr_GetTotalNumberOfUsers (HieLvl);

      /***** Get total number of users who want to be
	     notified by email on some event, from database *****/
      NumUsrsTotalWhoWantToBeNotifiedByEMailAboutSomeEvent =
      Usr_DB_GetNumUsrsWhoChoseAnOption (HieLvl,"usr_data.EmailNtfEvents<>0");

      /***** For each notify event... *****/
      for (NotifyEvent  = (Ntf_NotifyEvent_t) 1;
	   NotifyEvent <= (Ntf_NotifyEvent_t) (Ntf_NUM_NOTIFY_EVENTS - 1);
	   NotifyEvent++) // 0 is reserved for Ntf_EVENT_UNKNOWN
	{
	 /* Get the number of users who want to be notified by email on this event, from database */
	 if (asprintf (&SubQuery,"((usr_data.EmailNtfEvents & %u)<>0)",
		       (1 << NotifyEvent)) < 0)
	    Err_NotEnoughMemoryExit ();
	 NumUsrs[NotifyEvent] = Usr_DB_GetNumUsrsWhoChoseAnOption (HieLvl,SubQuery);
	 free (SubQuery);

	 /* Get number of notifications by email from database */
	 if (Ntf_DB_GetNumNotifs (&mysql_res,HieLvl,NotifyEvent))
	   {
	    row = mysql_fetch_row (mysql_res);

	    /* Get number of events notified */
	    if (row[0])
	      {
	       if (sscanf (row[0],"%u",&NumEvents[NotifyEvent]) != 1)
		  Err_ShowErrorAndExit ("Error when getting the number of notifications by email.");
	      }
	    else
	       NumEvents[NotifyEvent] = 0;

	    /* Get number of mails sent */
	    if (row[1])
	      {
	       if (sscanf (row[1],"%u",&NumMails[NotifyEvent]) != 1)
		  Err_ShowErrorAndExit ("Error when getting the number of emails to notify events3.");
	      }
	    else
	       NumMails[NotifyEvent] = 0;
	   }
	 else
	   {
	    NumEvents[NotifyEvent] = 0;
	    NumMails[NotifyEvent]  = 0;
	   }

	 /* Free structure that stores the query result */
	 DB_FreeMySQLResult (&mysql_res);

	 /* Update total number of events and mails */
	 NumEventsTotal += NumEvents[NotifyEvent];
	 NumMailsTotal  += NumMails [NotifyEvent];
	}

      /***** Write number of users who want to be notified by email on each event *****/
      for (NotifyEvent  = (Ntf_NotifyEvent_t) 1;
	   NotifyEvent <= (Ntf_NotifyEvent_t) (Ntf_NUM_NOTIFY_EVENTS - 1);
	   NotifyEvent++) // 0 is reserved for Ntf_EVENT_UNKNOWN
	{
	 HTM_TR_Begin (NULL);
	    HTM_TD_Txt_Left (Txt_NOTIFY_EVENTS_PLURAL[NotifyEvent]);
	    HTM_TD_Unsigned (NumUsrs[NotifyEvent]);
	    HTM_TD_Percentage (NumUsrs[NotifyEvent],NumUsrsTotal );
	    HTM_TD_Unsigned (NumEvents[NotifyEvent]);
	    HTM_TD_Unsigned (NumMails[NotifyEvent]);
	 HTM_TR_End ();
	}

      /***** Write total number of users who want to be notified by email on some event *****/
      HTM_TR_Begin (NULL);
	 HTM_TD_LINE_TOP_Txt (Txt_Total);
	 HTM_TD_LINE_TOP_Unsigned (NumUsrsTotalWhoWantToBeNotifiedByEMailAboutSomeEvent);
	 HTM_TD_LINE_TOP_Percentage (NumUsrsTotalWhoWantToBeNotifiedByEMailAboutSomeEvent,NumUsrsTotal);
	 HTM_TD_LINE_TOP_Unsigned (NumEventsTotal);
	 HTM_TD_LINE_TOP_Unsigned (NumMailsTotal);
      HTM_TR_End ();

   /***** End table and box *****/
   Box_BoxTableEnd ();
  }
