// swad_connected.c: connected users

/*
    SWAD (Shared Workspace At a Distance),
    is a web platform developed at the University of Granada (Spain),
    and used to support university teaching.

    This file is part of SWAD core.
    Copyright (C) 1999-2019 Antonio Caņas Vargas

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/*********************************** Headers *********************************/
/*****************************************************************************/

#include <limits.h>		// For maximum values
#include <linux/limits.h>	// For PATH_MAX
#include <linux/stddef.h>	// For NULL
#include <stdio.h>		// For fprintf
#include <string.h>		// For string functions

#include "swad_box.h"
#include "swad_database.h"
#include "swad_form.h"
#include "swad_global.h"
#include "swad_hierarchy.h"
#include "swad_parameter.h"
#include "swad_photo.h"
#include "swad_string.h"
#include "swad_table.h"
#include "swad_user.h"

/*****************************************************************************/
/*************************** Internal constants ******************************/
/*****************************************************************************/

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

extern struct Globals Gbl;

/*****************************************************************************/
/*************************** Internal prototypes *****************************/
/*****************************************************************************/

static void Con_PutIconToUpdateConnected (void);

static void Con_ShowGlobalConnectedUsrsRole (Rol_Role_t Role,unsigned UsrsTotal);

static void Con_ComputeConnectedUsrsWithARoleBelongingToCurrentCrs (Rol_Role_t Role);
static void Con_ShowConnectedUsrsBelongingToLocation (void);

static void Con_ShowConnectedUsrsWithARoleBelongingToCurrentLocationOnMainZone (Rol_Role_t Role);
static void Con_ShowConnectedUsrsWithARoleBelongingToCurrentCrsOnRightColumn (Rol_Role_t Role);
static unsigned Con_GetConnectedUsrsTotal (Rol_Role_t Role);

static void Con_GetNumConnectedUsrsWithARoleBelongingCurrentLocation (Rol_Role_t Role,struct ConnectedUsrs *Usrs);
static void Con_ComputeConnectedUsrsWithARoleCurrentCrsOneByOne (Rol_Role_t Role);
static void Con_ShowConnectedUsrsCurrentCrsOneByOneOnRightColumn (Rol_Role_t Role);
static void Con_WriteRowConnectedUsrOnRightColumn (Rol_Role_t Role);
static void Con_ShowConnectedUsrsCurrentLocationOneByOneOnMainZone (Rol_Role_t Role);

/*****************************************************************************/
/************************** Show connected users *****************************/
/*****************************************************************************/

void Con_ShowConnectedUsrs (void)
  {
   extern const char *Hlp_USERS_Connected;
   extern const char *Txt_Connected_users;

   /***** Link to show last clicks in real time *****/
   if (Gbl.Usrs.Me.Logged)
     {
      fprintf (Gbl.F.Out,"<div class=\"CONTEXT_MENU\">");
      Sta_PutLinkToLastClicks ();
      fprintf (Gbl.F.Out,"</div>");
     }

   /***** Get scope *****/
   Sco_SetScopesForListingStudents ();
   Sco_GetScope ("ScopeCon");

   /***** Start box *****/
   /* Current time */
   snprintf (Gbl.Title,sizeof (Gbl.Title),
	     "%s"
	     "<div id=\"connected_current_time\"></div>",
	     Txt_Connected_users);
   Box_StartBox (NULL,Gbl.Title,Con_PutIconToUpdateConnected,
		 Hlp_USERS_Connected,Box_NOT_CLOSABLE);
   fprintf (Gbl.F.Out,"<script type=\"text/javascript\">"
                      "writeLocalDateHMSFromUTC('connected_current_time',%ld,"
                      "%u,',&nbsp;',null,false,true,0x7);"
                      "</script>"
	              "</td>",
            (long) Gbl.StartExecutionTimeUTC,
            (unsigned) Gbl.Prefs.DateFormat);

   /***** Number of connected users in the whole platform *****/
   Con_ShowGlobalConnectedUsrs ();

   /***** Show connected users in the current location *****/
   if (Gbl.Scope.Current != Hie_UNK)
      Con_ShowConnectedUsrsBelongingToLocation ();

   /***** End box *****/
   Box_EndBox ();
  }

/*****************************************************************************/
/******************** Put icon to update connected users *********************/
/*****************************************************************************/

static void Con_PutIconToUpdateConnected (void)
  {
   extern const char *Txt_Update;

   Frm_StartForm (ActLstCon);
   Sco_PutParamScope ("ScopeCon",Gbl.Scope.Current);
   Frm_LinkFormSubmitAnimated (Txt_Update,NULL,NULL);
   Ico_PutCalculateIcon (Txt_Update);
   Frm_EndForm ();
  }

/*****************************************************************************/
/******************** Show total number of connected users *******************/
/*****************************************************************************/

void Con_ShowGlobalConnectedUsrs (void)
  {
   extern const char *Txt_Connected_users;
   extern const char *Txt_session;
   extern const char *Txt_sessions;
   extern const char *Txt_user[Usr_NUM_SEXS];
   extern const char *Txt_users[Usr_NUM_SEXS];
   Rol_Role_t Role;
   unsigned NumUsrs[Rol_NUM_ROLES];
   unsigned NumUsrsTotal;

   /***** Get number of connected users *****/
   for (Role  = Rol_GST, NumUsrsTotal = 0;
	Role <= Rol_SYS_ADM;
	Role++)
     {
      NumUsrs[Role] = Con_GetConnectedUsrsTotal (Role);
      NumUsrsTotal += NumUsrs[Role];
     }

   /***** Container start *****/
   fprintf (Gbl.F.Out,"<div class=\"CONNECTED\">");

   /***** Number of sessions *****/
   /* Link to view more details about connected users */
   Frm_StartFormUnique (ActLstCon);	// Must be unique because
					// the list of connected users
					// is dynamically updated via AJAX
   Frm_LinkFormSubmitUnique (Txt_Connected_users,"CONNECTED_TXT");

   /* Write total number of sessions */
   fprintf (Gbl.F.Out,"%u %s",
            Gbl.Session.NumSessions,
            (Gbl.Session.NumSessions == 1) ? Txt_session :
        	                             Txt_sessions);
   /* End link to view more details about connected users */
   fprintf (Gbl.F.Out,"</a>");
   Frm_EndForm ();

   if (NumUsrsTotal)
     {
      fprintf (Gbl.F.Out,"<div class=\"CONNECTED_LIST\">");

      /***** Write total number of users *****/
      fprintf (Gbl.F.Out,"%u %s:",
	       NumUsrsTotal,
	       (NumUsrsTotal == 1) ? Txt_user[Usr_SEX_UNKNOWN] :
				     Txt_users[Usr_SEX_UNKNOWN]);

      /***** Write total number of users with each role *****/
      for (Role  = Rol_GST, NumUsrsTotal = 0;
	   Role <= Rol_SYS_ADM;
	   Role++)
	 Con_ShowGlobalConnectedUsrsRole (Role,NumUsrs[Role]);

      fprintf (Gbl.F.Out,"</div>");
     }

   /***** Container end *****/
   fprintf (Gbl.F.Out,"</div>");
  }

static void Con_ShowGlobalConnectedUsrsRole (Rol_Role_t Role,unsigned UsrsTotal)
  {
   extern const char *Txt_ROLES_SINGUL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
   extern const char *Txt_ROLES_PLURAL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS];

   if (UsrsTotal)
      fprintf (Gbl.F.Out,"<br />"
			 "%u %s",
	       UsrsTotal,
	       (UsrsTotal == 1) ? Txt_ROLES_SINGUL_abc[Role][Usr_SEX_UNKNOWN] :
				  Txt_ROLES_PLURAL_abc[Role][Usr_SEX_UNKNOWN]);
  }

/*****************************************************************************/
/*********** Compute connected users who belong to current course ************/
/*****************************************************************************/

void Con_ComputeConnectedUsrsBelongingToCurrentCrs (void)
  {
   if ((Gbl.Prefs.SideCols & Lay_SHOW_RIGHT_COLUMN) &&	// Right column visible
       Gbl.Hierarchy.Level == Hie_CRS &&		// Course selected
       (Gbl.Usrs.Me.IBelongToCurrentCrs ||		// I can view users
        Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM))
     {
      Gbl.Usrs.Connected.NumUsrs       = 0;
      Gbl.Usrs.Connected.NumUsrsToList = 0;
      Gbl.Scope.Current = Hie_CRS;

      /***** Number of teachers *****/
      Con_ComputeConnectedUsrsWithARoleBelongingToCurrentCrs (Rol_TCH);

      /***** Number of non-editing teachers *****/
      Con_ComputeConnectedUsrsWithARoleBelongingToCurrentCrs (Rol_NET);

      /***** Number of students *****/
      Con_ComputeConnectedUsrsWithARoleBelongingToCurrentCrs (Rol_STD);
     }
  }

/*****************************************************************************/
/** Compute number of connected users of a type who belong to current course */
/*****************************************************************************/

static void Con_ComputeConnectedUsrsWithARoleBelongingToCurrentCrs (Rol_Role_t Role)
  {
   /***** Get number of connected users who belong to current course *****/
   Con_GetNumConnectedUsrsWithARoleBelongingCurrentLocation (Role,&Gbl.Usrs.Connected.Usrs[Role]);

   /***** Get list connected users belonging to this course *****/
   Con_ComputeConnectedUsrsWithARoleCurrentCrsOneByOne (Role);
  }

/*****************************************************************************/
/****** Show number of connected users who belong to current location ********/
/*****************************************************************************/

static void Con_ShowConnectedUsrsBelongingToLocation (void)
  {
   extern const char *Txt_from;
   struct ConnectedUsrs Usrs;

   /***** Start container *****/
   fprintf (Gbl.F.Out,"<div class=\"CONNECTED\""
		      " style=\"margin-top:6px;\">");

   /***** Number of connected users who belong to scope *****/
   Con_GetNumConnectedUsrsWithARoleBelongingCurrentLocation (Rol_UNK,&Usrs);
   fprintf (Gbl.F.Out,"<div class=\"CONNECTED_TXT\">%u %s ",
	    Usrs.NumUsrs,
	    Txt_from);

   /* Put form to change scope */
   Frm_StartForm (ActLstCon);
   Sco_PutSelectorScope ("ScopeCon",true);
   Frm_EndForm ();

   fprintf (Gbl.F.Out,"</div>");

   /***** Number of teachers and students *****/
   fprintf (Gbl.F.Out,"<table class=\"CONNECTED_LIST\">");
   Con_ShowConnectedUsrsWithARoleBelongingToCurrentLocationOnMainZone (Rol_TCH);
   Con_ShowConnectedUsrsWithARoleBelongingToCurrentLocationOnMainZone (Rol_NET);
   Con_ShowConnectedUsrsWithARoleBelongingToCurrentLocationOnMainZone (Rol_STD);
   if (Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM)
      Con_ShowConnectedUsrsWithARoleBelongingToCurrentLocationOnMainZone (Rol_GST);
   fprintf (Gbl.F.Out,"</table>");

   /***** Put link to register students *****/
   Enr_CheckStdsAndPutButtonToRegisterStdsInCurrentCrs ();

   /***** End container *****/
   fprintf (Gbl.F.Out,"</div>");
  }

/*****************************************************************************/
/* Show number of connected users who belong to current course on right col. */
/*****************************************************************************/

void Con_ShowConnectedUsrsBelongingToCurrentCrs (void)
  {
   extern const char *Txt_Connected_users;
   extern const char *Txt_from;
   char CourseName[Hie_MAX_BYTES_SHRT_NAME + 1];
   struct ConnectedUsrs Usrs;

   /***** Trivial check *****/
   if (Gbl.Hierarchy.Crs.CrsCod <= 0)	// No course selected
      return;

   /***** Start container *****/
   fprintf (Gbl.F.Out,"<div class=\"CONNECTED\">");

   /***** Number of connected users who belong to course *****/
   /* Link to view more details about connected users */
   Frm_StartFormUnique (ActLstCon);	// Must be unique because
					// the list of connected users
					// is dynamically updated via AJAX
   Frm_LinkFormSubmitUnique (Txt_Connected_users,"CONNECTED_TXT");
   Str_Copy (CourseName,Gbl.Hierarchy.Crs.ShrtName,
             Hie_MAX_BYTES_SHRT_NAME);
   Con_GetNumConnectedUsrsWithARoleBelongingCurrentLocation (Rol_UNK,&Usrs);
   fprintf (Gbl.F.Out,"%u %s %s"
	              "</a>",
            Usrs.NumUsrs,Txt_from,CourseName);
   Frm_EndForm ();

   /***** Number of teachers and students *****/
   fprintf (Gbl.F.Out,"<table class=\"CONNECTED_LIST\">");
   Gbl.Usrs.Connected.NumUsr        = 0;
   Gbl.Usrs.Connected.NumUsrs       = 0;
   Gbl.Usrs.Connected.NumUsrsToList = 0;
   Con_ShowConnectedUsrsWithARoleBelongingToCurrentCrsOnRightColumn (Rol_TCH);
   Con_ShowConnectedUsrsWithARoleBelongingToCurrentCrsOnRightColumn (Rol_NET);
   Con_ShowConnectedUsrsWithARoleBelongingToCurrentCrsOnRightColumn (Rol_STD);
   fprintf (Gbl.F.Out,"</table>");

   /***** End container *****/
   fprintf (Gbl.F.Out,"</div>");
  }

/*****************************************************************************/
/* Show number of connected users with a role who belong to current location */
/*****************************************************************************/

static void Con_ShowConnectedUsrsWithARoleBelongingToCurrentLocationOnMainZone (Rol_Role_t Role)
  {
   extern const char *Txt_ROLES_SINGUL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
   extern const char *Txt_ROLES_PLURAL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
   struct ConnectedUsrs Usrs;

   /***** Write number of connected users who belong to current course *****/
   Con_GetNumConnectedUsrsWithARoleBelongingCurrentLocation (Role,&Usrs);
   if (Usrs.NumUsrs)
     {
      fprintf (Gbl.F.Out,"<tr>"
			 "<td colspan=\"3\" class=\"CENTER_TOP\">"
			 "%u %s"
			 "</td>"
			 "</tr>",
	       Usrs.NumUsrs,
	       (Usrs.NumUsrs == 1) ? Txt_ROLES_SINGUL_abc[Role][Usrs.Sex] :
				     Txt_ROLES_PLURAL_abc[Role][Usrs.Sex]);

      /***** I can see connected users *****/
      Con_ShowConnectedUsrsCurrentLocationOneByOneOnMainZone (Role);
     }
  }

/*****************************************************************************/
/** Show number of connected users with a role who belong to current course **/
/*****************************************************************************/

static void Con_ShowConnectedUsrsWithARoleBelongingToCurrentCrsOnRightColumn (Rol_Role_t Role)
  {
   extern const char *Txt_ROLES_SINGUL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
   extern const char *Txt_ROLES_PLURAL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
   extern const char *Txt_Connected_users;
   unsigned NumUsrsThisRole = Gbl.Usrs.Connected.Usrs[Role].NumUsrs;
   Usr_Sex_t UsrSex = Gbl.Usrs.Connected.Usrs[Role].Sex;

   if (NumUsrsThisRole)
     {
      /***** Write number of connected users who belong to current course *****/
      Gbl.Usrs.Connected.NumUsrs       += NumUsrsThisRole;
      Gbl.Usrs.Connected.NumUsrsToList += NumUsrsThisRole;
      if (Gbl.Usrs.Connected.NumUsrsToList > Cfg_MAX_CONNECTED_SHOWN)
	 Gbl.Usrs.Connected.NumUsrsToList = Cfg_MAX_CONNECTED_SHOWN;

      fprintf (Gbl.F.Out,"<tr>"
			 "<td colspan=\"3\" class=\"CON_USR_NARROW_TIT\">"
			 "%u %s"
			 "</td>"
			 "</tr>",
	       NumUsrsThisRole,
	       (NumUsrsThisRole == 1) ? Txt_ROLES_SINGUL_abc[Role][UsrSex] :
					Txt_ROLES_PLURAL_abc[Role][UsrSex]);

      /***** I can see connected users *****/
      Con_ShowConnectedUsrsCurrentCrsOneByOneOnRightColumn (Role);

      /***** Write message with number of users not listed *****/
      if (Gbl.Usrs.Connected.NumUsrsToList < Gbl.Usrs.Connected.NumUsrs)
	{
	 fprintf (Gbl.F.Out,"<tr>"
			    "<td colspan=\"3\" class=\"CENTER_TOP\">");
	 Frm_StartFormUnique (ActLstCon);	// Must be unique because
						// the list of connected users
						// is dynamically updated via AJAX
	 Sco_PutParamScope ("ScopeCon",Hie_CRS);
	 Frm_LinkFormSubmitUnique (Txt_Connected_users,"CONNECTED_TXT");
	 fprintf (Gbl.F.Out,"<img src=\"%s/ellipsis-h.svg\""
			    " alt=\"%s\" title=\"%s\" class=\"ICO40x40\" />"
			    "</a>",
		  Cfg_URL_ICON_PUBLIC,
		  Txt_Connected_users,Txt_Connected_users);
	 Frm_EndForm ();
	 fprintf (Gbl.F.Out,"</td>"
			    "</tr>");
	}
     }
  }

/*****************************************************************************/
/********************* Modify my entry in connected list *********************/
/*****************************************************************************/

void Con_UpdateMeInConnectedList (void)
  {
   /***** Update my entry in connected list.
          The role which is stored is the role of the last click *****/
   DB_QueryREPLACE ("can not update list of connected users",
		    "REPLACE INTO connected"
		    " (UsrCod,RoleInLastCrs,LastCrsCod,LastTime)"
		    " VALUES"
		    " (%ld,%u,%ld,NOW())",
                    Gbl.Usrs.Me.UsrDat.UsrCod,
                    (unsigned) Gbl.Usrs.Me.Role.Logged,
                    Gbl.Hierarchy.Crs.CrsCod);
  }

/*****************************************************************************/
/************************** Remove old connected uses ************************/
/*****************************************************************************/

void Con_RemoveOldConnected (void)
  {
   /***** Remove old users from connected list *****/
   DB_QueryDELETE ("can not remove old users from list of connected users",
		   "DELETE FROM connected WHERE UsrCod NOT IN"
		   " (SELECT DISTINCT(UsrCod) FROM sessions)");
  }

/*****************************************************************************/
/********************* Get connected users with a role ***********************/
/*****************************************************************************/

static unsigned Con_GetConnectedUsrsTotal (Rol_Role_t Role)
  {
   if (!Gbl.DB.DatabaseIsOpen)
      return 0;

   /***** Get number of connected users with a role from database *****/
   return
   (unsigned) DB_QueryCOUNT ("can not get number of connected users",
			     "SELECT COUNT(*) FROM connected"
			     " WHERE RoleInLastCrs=%u",
			     (unsigned) Role);
  }

/*****************************************************************************/
/************** Get connected users belonging to current course **************/
/*****************************************************************************/
// Return user's sex in UsrSex

static void Con_GetNumConnectedUsrsWithARoleBelongingCurrentLocation (Rol_Role_t Role,struct ConnectedUsrs *Usrs)
  {
   extern const char *Usr_StringsSexDB[Usr_NUM_SEXS];
   MYSQL_RES *mysql_res;
   MYSQL_ROW row;
   unsigned NumSexs;
   Usr_Sex_t Sex;

   /***** Get number of connected users who belong to current course from database *****/
   switch (Role)
     {
      case Rol_UNK:	// Here Rol_UNK means "any role"
	 switch (Gbl.Scope.Current)
	   {
	    case Hie_SYS:		// Show connected users in the whole platform
	       DB_QuerySELECT (&mysql_res,"can not get number"
					  " of connected users"
					  " who belong to this location",
			       "SELECT COUNT(DISTINCT connected.UsrCod),"
			       "COUNT(DISTINCT usr_data.Sex),MIN(usr_data.Sex)"
			       " FROM connected,usr_data"
			       " WHERE connected.UsrCod=usr_data.UsrCod");
	       break;
	    case Hie_CTY:		// Show connected users in the current country
	       DB_QuerySELECT (&mysql_res,"can not get number"
					  " of connected users"
					  " who belong to this location",
			       "SELECT COUNT(DISTINCT connected.UsrCod),"
			       "COUNT(DISTINCT usr_data.Sex),MIN(usr_data.Sex)"
			       " FROM institutions,centres,degrees,courses,crs_usr,connected,usr_data"
			       " WHERE institutions.CtyCod=%ld"
			       " AND institutions.InsCod=centres.InsCod"
			       " AND centres.CtrCod=degrees.CtrCod"
			       " AND degrees.DegCod=courses.DegCod"
			       " AND courses.CrsCod=crs_usr.CrsCod"
			       " AND crs_usr.UsrCod=connected.UsrCod"
			       " AND connected.UsrCod=usr_data.UsrCod",
			       Gbl.Hierarchy.Cty.CtyCod);
	       break;
	    case Hie_INS:		// Show connected users in the current institution
	       DB_QuerySELECT (&mysql_res,"can not get number"
					  " of connected users"
					  " who belong to this location",
			       "SELECT COUNT(DISTINCT connected.UsrCod),"
			       "COUNT(DISTINCT usr_data.Sex),MIN(usr_data.Sex)"
			       " FROM centres,degrees,courses,crs_usr,connected,usr_data"
			       " WHERE centres.InsCod=%ld"
			       " AND centres.CtrCod=degrees.CtrCod"
			       " AND degrees.DegCod=courses.DegCod"
			       " AND courses.CrsCod=crs_usr.CrsCod"
			       " AND crs_usr.UsrCod=connected.UsrCod"
			       " AND connected.UsrCod=usr_data.UsrCod",
			       Gbl.Hierarchy.Ins.InsCod);
	       break;
	    case Hie_CTR:		// Show connected users in the current centre
	       DB_QuerySELECT (&mysql_res,"can not get number"
					  " of connected users"
					  " who belong to this location",
			       "SELECT COUNT(DISTINCT connected.UsrCod),"
			       "COUNT(DISTINCT usr_data.Sex),MIN(usr_data.Sex)"
			       " FROM degrees,courses,crs_usr,connected,usr_data"
			       " WHERE degrees.CtrCod=%ld"
			       " AND degrees.DegCod=courses.DegCod"
			       " AND courses.CrsCod=crs_usr.CrsCod"
			       " AND crs_usr.UsrCod=connected.UsrCod"
			       " AND connected.UsrCod=usr_data.UsrCod",
			       Gbl.Hierarchy.Ctr.CtrCod);
	       break;
	    case Hie_DEG:		// Show connected users in the current degree
	       DB_QuerySELECT (&mysql_res,"can not get number"
					  " of connected users"
					  " who belong to this location",
			       "SELECT COUNT(DISTINCT connected.UsrCod),"
			       "COUNT(DISTINCT usr_data.Sex),MIN(usr_data.Sex)"
			       " FROM courses,crs_usr,connected,usr_data"
			       " WHERE courses.DegCod=%ld"
			       " AND courses.CrsCod=crs_usr.CrsCod"
			       " AND crs_usr.UsrCod=connected.UsrCod"
			       " AND connected.UsrCod=usr_data.UsrCod",
			       Gbl.Hierarchy.Deg.DegCod);
	       break;
	    case Hie_CRS:		// Show connected users in the current course
	       DB_QuerySELECT (&mysql_res,"can not get number"
					  " of connected users"
					  " who belong to this location",
			       "SELECT COUNT(DISTINCT connected.UsrCod),"
			       "COUNT(DISTINCT usr_data.Sex),MIN(usr_data.Sex)"
			       " FROM crs_usr,connected,usr_data"
			       " WHERE crs_usr.CrsCod=%ld"
			       " AND crs_usr.UsrCod=connected.UsrCod"
			       " AND connected.UsrCod=usr_data.UsrCod",
			       Gbl.Hierarchy.Crs.CrsCod);
	       break;
	    default:
	       Lay_WrongScopeExit ();
	       break;
	   }
	 break;
      case Rol_GST:
	 DB_QuerySELECT (&mysql_res,"can not get number"
				    " of connected users"
				    " who belong to this location",
			 "SELECT COUNT(DISTINCT connected.UsrCod),"
			 "COUNT(DISTINCT usr_data.Sex),MIN(usr_data.Sex)"
			 " FROM connected,usr_data"
			 " WHERE connected.UsrCod NOT IN (SELECT UsrCod FROM crs_usr)"
			 " AND connected.UsrCod=usr_data.UsrCod");
	 break;
      case Rol_STD:
      case Rol_NET:
      case Rol_TCH:
	 switch (Gbl.Scope.Current)
	   {
	    case Hie_SYS:		// Show connected users in the whole platform
	       DB_QuerySELECT (&mysql_res,"can not get number"
					  " of connected users"
					  " who belong to this location",
			       "SELECT COUNT(DISTINCT connected.UsrCod),"
			       "COUNT(DISTINCT usr_data.Sex),MIN(usr_data.Sex)"
			       " FROM connected,crs_usr,usr_data"
			       " WHERE connected.UsrCod=crs_usr.UsrCod"
			       " AND crs_usr.Role=%u"
			       " AND connected.UsrCod=usr_data.UsrCod",
			       (unsigned) Role);
	       break;
	    case Hie_CTY:		// Show connected users in the current country
	       DB_QuerySELECT (&mysql_res,"can not get number"
					      " of connected users"
					      " who belong to this location",
			       "SELECT COUNT(DISTINCT connected.UsrCod),"
			       "COUNT(DISTINCT usr_data.Sex),MIN(usr_data.Sex)"
			       " FROM institutions,centres,degrees,courses,crs_usr,connected,usr_data"
			       " WHERE institutions.CtyCod=%ld"
			       " AND institutions.InsCod=centres.InsCod"
			       " AND centres.CtrCod=degrees.CtrCod"
			       " AND degrees.DegCod=courses.DegCod"
			       " AND courses.CrsCod=crs_usr.CrsCod"
			       " AND crs_usr.Role=%u"
			       " AND crs_usr.UsrCod=connected.UsrCod"
			       " AND connected.UsrCod=usr_data.UsrCod",
			       Gbl.Hierarchy.Cty.CtyCod,
			       (unsigned) Role);
	       break;
	    case Hie_INS:		// Show connected users in the current institution
	       DB_QuerySELECT (&mysql_res,"can not get number"
					  " of connected users"
					  " who belong to this location",
			       "SELECT COUNT(DISTINCT connected.UsrCod),"
			       "COUNT(DISTINCT usr_data.Sex),MIN(usr_data.Sex)"
			       " FROM centres,degrees,courses,crs_usr,connected,usr_data"
			       " WHERE centres.InsCod=%ld"
			       " AND centres.CtrCod=degrees.CtrCod"
			       " AND degrees.DegCod=courses.DegCod"
			       " AND courses.CrsCod=crs_usr.CrsCod"
			       " AND crs_usr.Role=%u"
			       " AND crs_usr.UsrCod=connected.UsrCod"
			       " AND connected.UsrCod=usr_data.UsrCod",
			       Gbl.Hierarchy.Ins.InsCod,
			       (unsigned) Role);
	       break;
	    case Hie_CTR:		// Show connected users in the current centre
	       DB_QuerySELECT (&mysql_res,"can not get number"
					  " of connected users"
					  " who belong to this location",
			       "SELECT COUNT(DISTINCT connected.UsrCod),"
			       "COUNT(DISTINCT usr_data.Sex),MIN(usr_data.Sex)"
			       " FROM degrees,courses,crs_usr,connected,usr_data"
			       " WHERE degrees.CtrCod=%ld"
			       " AND degrees.DegCod=courses.DegCod"
			       " AND courses.CrsCod=crs_usr.CrsCod"
			       " AND crs_usr.Role=%u"
			       " AND crs_usr.UsrCod=connected.UsrCod"
			       " AND connected.UsrCod=usr_data.UsrCod",
			       Gbl.Hierarchy.Ctr.CtrCod,
			       (unsigned) Role);
	       break;
	    case Hie_DEG:		// Show connected users in the current degree
	       DB_QuerySELECT (&mysql_res,"can not get number"
					  " of connected users"
					  " who belong to this location",
			       "SELECT COUNT(DISTINCT connected.UsrCod),"
			       "COUNT(DISTINCT usr_data.Sex),MIN(usr_data.Sex)"
			       " FROM courses,crs_usr,connected,usr_data"
			       " WHERE courses.DegCod=%ld"
			       " AND courses.CrsCod=crs_usr.CrsCod"
			       " AND crs_usr.Role=%u"
			       " AND crs_usr.UsrCod=connected.UsrCod"
			       " AND connected.UsrCod=usr_data.UsrCod",
			       Gbl.Hierarchy.Deg.DegCod,
			       (unsigned) Role);
	       break;
	    case Hie_CRS:		// Show connected users in the current course
	       DB_QuerySELECT (&mysql_res,"can not get number"
					  " of connected users"
					  " who belong to this location",
			       "SELECT COUNT(DISTINCT connected.UsrCod),"
			       "COUNT(DISTINCT usr_data.Sex),MIN(usr_data.Sex)"
			       " FROM crs_usr,connected,usr_data"
			       " WHERE crs_usr.CrsCod=%ld"
			       " AND crs_usr.Role=%u"
			       " AND crs_usr.UsrCod=connected.UsrCod"
			       " AND connected.UsrCod=usr_data.UsrCod",
			       Gbl.Hierarchy.Crs.CrsCod,
			       (unsigned) Role);
	       break;
	    default:
	       Lay_WrongScopeExit ();
	       break;
	   }
	 break;
      default:
	 Lay_ShowErrorAndExit ("Wrong role.");
	 break;
     }

   row = mysql_fetch_row (mysql_res);

   /***** Get number of users (row[0]) *****/
   if (sscanf (row[0],"%u",&(Usrs->NumUsrs)) != 1)
      Lay_ShowErrorAndExit ("Error when getting number of connected users who belong to this location.");

   /***** Get number of distinct sexs (row[1]) *****/
   if (sscanf (row[1],"%u",&NumSexs) != 1)
      Lay_ShowErrorAndExit ("Error when getting number of sexs in connected users who belong to this location.");

   /***** Get users' sex (row[2]) *****/
   Usrs->Sex = Usr_SEX_UNKNOWN;
   if (NumSexs == 1)
      for (Sex = (Usr_Sex_t) 0;
	   Sex < Usr_NUM_SEXS;
	   Sex++)
         if (!strcasecmp (row[2],Usr_StringsSexDB[Sex]))
           {
            Usrs->Sex = Sex;
            break;
           }

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

/*****************************************************************************/
/******************* Compute connected users one by one **********************/
/*****************************************************************************/

static void Con_ComputeConnectedUsrsWithARoleCurrentCrsOneByOne (Rol_Role_t Role)
  {
   MYSQL_RES *mysql_res;
   MYSQL_ROW row;
   unsigned long NumRows;
   unsigned NumUsr = Gbl.Usrs.Connected.NumUsrs;	// Save current number of users

   /***** Get connected users who belong to current course from database *****/
   NumRows = DB_QuerySELECT (&mysql_res,"can not get list of connected users"
					" who belong to this course",
			     "SELECT connected.UsrCod,connected.LastCrsCod,"
			     "UNIX_TIMESTAMP()-UNIX_TIMESTAMP(connected.LastTime) AS Dif"
			     " FROM connected,crs_usr"
			     " WHERE crs_usr.CrsCod=%ld AND crs_usr.Role=%u"
			     " AND crs_usr.UsrCod=connected.UsrCod"
			     " ORDER BY Dif",
			     Gbl.Hierarchy.Crs.CrsCod,
			     (unsigned) Role);
   Gbl.Usrs.Connected.NumUsrs       += (unsigned) NumRows;
   Gbl.Usrs.Connected.NumUsrsToList += (unsigned) NumRows;
   if (Gbl.Usrs.Connected.NumUsrsToList > Cfg_MAX_CONNECTED_SHOWN)
      Gbl.Usrs.Connected.NumUsrsToList = Cfg_MAX_CONNECTED_SHOWN;

   /***** Write list of connected users *****/
   for (;
	NumUsr < Gbl.Usrs.Connected.NumUsrsToList;
	NumUsr++)
     {
      row = mysql_fetch_row (mysql_res);

      /* Get user code (row[0]) */
      Gbl.Usrs.Connected.Lst[NumUsr].UsrCod = Str_ConvertStrCodToLongCod (row[0]);

      /* Get course code (row[1]) */
      Gbl.Usrs.Connected.Lst[NumUsr].ThisCrs = (Str_ConvertStrCodToLongCod (row[1]) ==
	                                        Gbl.Hierarchy.Crs.CrsCod);

      /* Compute elapsed time from last access */
      if (sscanf (row[2],"%ld",&Gbl.Usrs.Connected.Lst[NumUsr].TimeDiff) != 1)
         Gbl.Usrs.Connected.Lst[NumUsr].TimeDiff = (time_t) 0;
     }

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

/*****************************************************************************/
/******************* Show connected users one by one *************************/
/*****************************************************************************/

static void Con_ShowConnectedUsrsCurrentCrsOneByOneOnRightColumn (Rol_Role_t Role)
  {
   /***** Write list of connected users *****/
   for (;
	Gbl.Usrs.Connected.NumUsr < Gbl.Usrs.Connected.NumUsrsToList;
	Gbl.Usrs.Connected.NumUsr++)
      /* Write row in screen */
      Con_WriteRowConnectedUsrOnRightColumn (Role);
  }

/*****************************************************************************/
/********************* Write the name of a connected user ********************/
/*****************************************************************************/

static void Con_WriteRowConnectedUsrOnRightColumn (Rol_Role_t Role)
  {
   extern const char *Txt_View_record_for_this_course;
   bool ShowPhoto;
   char PhotoURL[PATH_MAX + 1];
   const char *Font;
   long UsrCod;
   bool ItsMe;
   struct UsrData *UsrDat;
   struct UsrData OtherUsrDat;

   /***** Get user's code from list *****/
   UsrCod = Gbl.Usrs.Connected.Lst[Gbl.Usrs.Connected.NumUsr].UsrCod;
   ItsMe = Usr_ItsMe (UsrCod);

   if (ItsMe)
      UsrDat = &Gbl.Usrs.Me.UsrDat;
   else
     {
      /***** Initialize structure with user's data *****/
      OtherUsrDat.UsrCod = UsrCod;
      Usr_UsrDataConstructor (&OtherUsrDat);

      /***** Get user's data *****/
      Usr_GetAllUsrDataFromUsrCod (&OtherUsrDat,Usr_DONT_GET_PREFS);

      UsrDat = &OtherUsrDat;
     }

   /***** Show photo *****/
   fprintf (Gbl.F.Out,"<tr>"
	              "<td class=\"CON_PHOTO COLOR%u\">",
	    Gbl.RowEvenOdd);
   ShowPhoto = Pho_ShowingUsrPhotoIsAllowed (UsrDat,PhotoURL);
   Pho_ShowUsrPhoto (UsrDat,ShowPhoto ? PhotoURL :
                	                NULL,
                     "PHOTO21x28",Pho_ZOOM,true);
   fprintf (Gbl.F.Out,"</td>");

   /***** Write full name and link *****/
   Font = (Gbl.Usrs.Connected.Lst[Gbl.Usrs.Connected.NumUsr].ThisCrs ? "CON_NAME_NARROW CON_CRS" :
	                                                               "CON_NAME_NARROW CON_NO_CRS");
   fprintf (Gbl.F.Out,"<td class=\"%s COLOR%u\">",
	    Font,Gbl.RowEvenOdd);
   // The form must be unique because
   // the list of connected users
   // is dynamically updated via AJAX
   switch (Role)
     {
      case Rol_STD:
	 Frm_StartFormUnique (ActSeeRecOneStd);
	 break;
      case Rol_NET:
      case Rol_TCH:
	 Frm_StartFormUnique (ActSeeRecOneTch);
	 break;
      default:
	 Lay_ShowErrorAndExit ("Wrong role.");
	 break;
     }
   Usr_PutParamUsrCodEncrypted (UsrDat->EncryptedUsrCod);
   fprintf (Gbl.F.Out,"<div class=\"CON_NAME_NARROW\">");	// Limited width
   Frm_LinkFormSubmitUnique (Txt_View_record_for_this_course,Font);
   Usr_WriteFirstNameBRSurnames (UsrDat);
   fprintf (Gbl.F.Out,"</a>"
	              "</div>");
   Frm_EndForm ();
   fprintf (Gbl.F.Out,"</td>");

   /***** Write time from last access *****/
   Font = (Gbl.Usrs.Connected.Lst[Gbl.Usrs.Connected.NumUsr].ThisCrs ? "CON_SINCE CON_CRS" :
	                                                               "CON_SINCE CON_NO_CRS");

   fprintf (Gbl.F.Out,"<td class=\"%s COLOR%u\">",
            Font,Gbl.RowEvenOdd);

   fprintf (Gbl.F.Out,"<div id=\"hm%u\">",
            Gbl.Usrs.Connected.NumUsr);	// Used for automatic update, only when displayed on right column
   Dat_WriteHoursMinutesSecondsFromSeconds (Gbl.Usrs.Connected.Lst[Gbl.Usrs.Connected.NumUsr].TimeDiff);
   fprintf (Gbl.F.Out,"</div>");	// Used for automatic update, only when displayed on right column

   fprintf (Gbl.F.Out,"</td>"
	              "</tr>");

   if (!ItsMe)
      /***** Free memory used for user's data *****/
      Usr_UsrDataDestructor (&OtherUsrDat);

   Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd;
  }

/*****************************************************************************/
/******************** Show connected users one by one ************************/
/*****************************************************************************/

static void Con_ShowConnectedUsrsCurrentLocationOneByOneOnMainZone (Rol_Role_t Role)
  {
   MYSQL_RES *mysql_res;
   MYSQL_ROW row;
   unsigned NumUsrs = 0;	// Initialized to avoid warning
   unsigned NumUsr;
   bool ThisCrs;
   time_t TimeDiff;
   bool ShowPhoto;
   char PhotoURL[PATH_MAX + 1];
   const char *Font;
   struct UsrData UsrDat;
   bool PutLinkToRecord = (Gbl.Hierarchy.Level == Hie_CRS &&	// Course selected
	                   Gbl.Scope.Current   == Hie_CRS &&	// Scope is current course
	                   (Role == Rol_STD ||			// Role is student,...
	                    Role == Rol_NET ||			// ...non-editing teacher...
	                    Role == Rol_TCH));			// ...or teacher

   /***** Get connected users who belong to current location from database *****/
   switch (Role)
     {
      case Rol_GST:
	 NumUsrs = (unsigned) DB_QuerySELECT (&mysql_res,"can not get list of connected users"
	                                                       " who belong to this location",
					      "SELECT UsrCod,LastCrsCod,"
					      "UNIX_TIMESTAMP()-UNIX_TIMESTAMP(LastTime) AS Dif"
					      " FROM connected"
					      " WHERE UsrCod NOT IN (SELECT UsrCod FROM crs_usr)"
					      " ORDER BY Dif");
	 break;
      case Rol_STD:
      case Rol_NET:
      case Rol_TCH:
	 switch (Gbl.Scope.Current)
	   {
	    case Hie_SYS:		// Show connected users in the whole platform
	       NumUsrs = (unsigned) DB_QuerySELECT (&mysql_res,"can not get list of connected users"
	                                                       " who belong to this location",
						    "SELECT DISTINCTROW connected.UsrCod,connected.LastCrsCod,"
						    "UNIX_TIMESTAMP()-UNIX_TIMESTAMP(connected.LastTime) AS Dif"
						    " FROM connected,crs_usr"
						    " WHERE connected.UsrCod=crs_usr.UsrCod"
						    " AND crs_usr.Role=%u"
						    " ORDER BY Dif",
						    (unsigned) Role);
	       break;
	    case Hie_CTY:		// Show connected users in the current country
	       NumUsrs = (unsigned) DB_QuerySELECT (&mysql_res,"can not get list of connected users"
	                                                       " who belong to this location",
						    "SELECT DISTINCTROW connected.UsrCod,connected.LastCrsCod,"
						    "UNIX_TIMESTAMP()-UNIX_TIMESTAMP(connected.LastTime) AS Dif"
						    " FROM institutions,centres,degrees,courses,crs_usr,connected"
						    " WHERE institutions.CtyCod=%ld"
						    " AND institutions.InsCod=centres.InsCod"
						    " AND centres.CtrCod=degrees.CtrCod"
						    " AND degrees.DegCod=courses.DegCod"
						    " AND courses.CrsCod=crs_usr.CrsCod"
						    " AND crs_usr.Role=%u"
						    " AND crs_usr.UsrCod=connected.UsrCod"
						    " ORDER BY Dif",
						    Gbl.Hierarchy.Cty.CtyCod,
						    (unsigned) Role);
	       break;
	    case Hie_INS:		// Show connected users in the current institution
	       NumUsrs = (unsigned) DB_QuerySELECT (&mysql_res,"can not get list of connected users"
	                                                       " who belong to this location",
						    "SELECT DISTINCTROW connected.UsrCod,connected.LastCrsCod,"
						    "UNIX_TIMESTAMP()-UNIX_TIMESTAMP(connected.LastTime) AS Dif"
						    " FROM centres,degrees,courses,crs_usr,connected"
						    " WHERE centres.InsCod=%ld"
						    " AND centres.CtrCod=degrees.CtrCod"
						    " AND degrees.DegCod=courses.DegCod"
						    " AND courses.CrsCod=crs_usr.CrsCod"
						    " AND crs_usr.Role=%u"
						    " AND crs_usr.UsrCod=connected.UsrCod"
						    " ORDER BY Dif",
						    Gbl.Hierarchy.Ins.InsCod,
						    (unsigned) Role);
	       break;
	    case Hie_CTR:		// Show connected users in the current centre
	       NumUsrs = (unsigned) DB_QuerySELECT (&mysql_res,"can not get list of connected users"
	                                                       " who belong to this location",
						    "SELECT DISTINCTROW connected.UsrCod,connected.LastCrsCod,"
						    "UNIX_TIMESTAMP()-UNIX_TIMESTAMP(connected.LastTime) AS Dif"
						    " FROM degrees,courses,crs_usr,connected"
						    " WHERE degrees.CtrCod=%ld"
						    " AND degrees.DegCod=courses.DegCod"
						    " AND courses.CrsCod=crs_usr.CrsCod"
						    " AND crs_usr.Role=%u"
						    " AND crs_usr.UsrCod=connected.UsrCod"
						    " ORDER BY Dif",
						    Gbl.Hierarchy.Ctr.CtrCod,
						    (unsigned) Role);
	       break;
	    case Hie_DEG:		// Show connected users in the current degree
	       NumUsrs = (unsigned) DB_QuerySELECT (&mysql_res,"can not get list of connected users"
	                                                       " who belong to this location",
						    "SELECT DISTINCTROW connected.UsrCod,connected.LastCrsCod,"
						    "UNIX_TIMESTAMP()-UNIX_TIMESTAMP(connected.LastTime) AS Dif"
						    " FROM courses,crs_usr,connected"
						    " WHERE courses.DegCod=%ld"
						    " AND courses.CrsCod=crs_usr.CrsCod"
						    " AND crs_usr.Role=%u"
						    " AND crs_usr.UsrCod=connected.UsrCod"
						    " ORDER BY Dif",
						    Gbl.Hierarchy.Deg.DegCod,
						    (unsigned) Role);
	       break;
	    case Hie_CRS:		// Show connected users in the current course
	       NumUsrs = (unsigned) DB_QuerySELECT (&mysql_res,"can not get list of connected users"
	                                                       " who belong to this location",
						    "SELECT connected.UsrCod,connected.LastCrsCod,"
						    "UNIX_TIMESTAMP()-UNIX_TIMESTAMP(connected.LastTime) AS Dif"
						    " FROM crs_usr,connected"
						    " WHERE crs_usr.CrsCod=%ld"
						    " AND crs_usr.Role=%u"
						    " AND crs_usr.UsrCod=connected.UsrCod"
						    " ORDER BY Dif",
						    Gbl.Hierarchy.Crs.CrsCod,
						    (unsigned) Role);
	       break;
	    default:
	       Lay_WrongScopeExit ();
	       break;
	   }
	 break;
      default:
	 Lay_ShowErrorAndExit ("Wrong role.");
	 break;
     }
   if (NumUsrs)
     {
      /***** Initialize structure with user's data *****/
      Usr_UsrDataConstructor (&UsrDat);

      /***** Write list of connected users *****/
      for (NumUsr = 0;
	   NumUsr < NumUsrs;
	   NumUsr++)
        {
         row = mysql_fetch_row (mysql_res);

         /* Get user's data */
         UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0]);
         if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&UsrDat,Usr_DONT_GET_PREFS))        // Existing user
           {
	    /* Get course code (row[1]) */
	    ThisCrs = (Str_ConvertStrCodToLongCod (row[1]) ==
		       Gbl.Hierarchy.Crs.CrsCod);

	    /* Compute time from last access */
	    if (sscanf (row[2],"%ld",&TimeDiff) != 1)
	       TimeDiff = (time_t) 0;

	    /***** Show photo *****/
	    fprintf (Gbl.F.Out,"<tr>"
			       "<td class=\"CON_PHOTO COLOR%u\">",
		     Gbl.RowEvenOdd);
	    ShowPhoto = Pho_ShowingUsrPhotoIsAllowed (&UsrDat,PhotoURL);
	    Pho_ShowUsrPhoto (&UsrDat,ShowPhoto ? PhotoURL :
						  NULL,
			      "PHOTO21x28",Pho_ZOOM,false);
	    fprintf (Gbl.F.Out,"</td>");

	    /***** Write full name and link *****/
	    Font = (ThisCrs ? "CON_NAME_WIDE CON_CRS" :
			      "CON_NAME_WIDE CON_NO_CRS");
	    fprintf (Gbl.F.Out,"<td class=\"%s COLOR%u\">",
		     Font,Gbl.RowEvenOdd);
	    if (PutLinkToRecord)
	      {
	       switch (Role)
		 {
		  case Rol_STD:
		     Frm_StartForm (ActSeeRecOneStd);
		     break;
		  case Rol_NET:
		  case Rol_TCH:
		     Frm_StartForm (ActSeeRecOneTch);
		     break;
		  default:
		     Lay_ShowErrorAndExit ("Wrong role.");
		 }
	       Usr_PutParamUsrCodEncrypted (UsrDat.EncryptedUsrCod);
	      }
            fprintf (Gbl.F.Out,"<div class=\"CON_NAME_WIDE\">");	// Limited width
	    if (PutLinkToRecord)
	       Frm_LinkFormSubmit (UsrDat.FullName,Font,NULL);
            Usr_WriteFirstNameBRSurnames (&UsrDat);
	    if (PutLinkToRecord)
	       fprintf (Gbl.F.Out,"</a>");
	    fprintf (Gbl.F.Out,"</div>");
	    if (PutLinkToRecord)
	       Frm_EndForm ();
	    fprintf (Gbl.F.Out,"</td>");

	    /***** Write time from last access *****/
	    Font = (ThisCrs ? "CON_SINCE CON_CRS" :
			      "CON_SINCE CON_NO_CRS");
	    fprintf (Gbl.F.Out,"<td class=\"%s COLOR%u\">",
		     Font,Gbl.RowEvenOdd);
	    Dat_WriteHoursMinutesSecondsFromSeconds (TimeDiff);
	    fprintf (Gbl.F.Out,"</td>"
			       "</tr>");

	    Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd;
	   }
        }

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

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

/*****************************************************************************/
/****** Write script to automatically update clocks of connected users *******/
/*****************************************************************************/

void Con_WriteScriptClockConnected (void)
  {
   unsigned NumUsr;

   fprintf (Gbl.F.Out,"	NumUsrsCon = %u;\n",
            Gbl.Usrs.Connected.NumUsrsToList);
   for (NumUsr = 0;
	NumUsr < Gbl.Usrs.Connected.NumUsrsToList;
	NumUsr++)
      fprintf (Gbl.F.Out,"	ListSeconds[%u] = %ld;\n",
               NumUsr,Gbl.Usrs.Connected.Lst[NumUsr].TimeDiff);
   fprintf (Gbl.F.Out,"	writeClockConnected();\n");
  }
