Jump to content

Recommended Posts

Hi All,

 

I have been searching for quite some time now for a script / process to check user online / offline status. 

 

I know there is no real "Ideal way" to check the status, as there could be a number of reasons for a user being "Offline / Online".

 

My search for an updated way to check this has been fruitless and I was hoping that someone could either direct me to where I could read up on this, or perhaps give me some guidance.

 

My thought process is that a user sessiontable would have to be created, this table will update when a user logs in in one column and logs out in another column... This way I can check when a user was last online, coupled with a current active session. The catch here is a user needs to physically logout... What of they don't logout? What if they go "Inactive" but are still online, so set an "Away" status.

 

Any advice / guidance will be greatly appreciated.

 

Thanks.

Link to comment
https://forums.phpfreaks.com/topic/299618-check-user-online-offline-status/
Share on other sites

I'd approach this from two directions:

 

First off, you should have a timestamp column which you update whenever the user makes a standard HTTP request. On logout, you set the value to NULL. If the last update is within a certain time frame (e. g. 15 minutes), you consider the user active.

 

Additionally, you should implement a “heartbeat” function in JavaScript. This function periodically makes an Ajax request (e. g. every 5 seconds), and the server saves the current timestamp in an additional column. If the last update is within a certain time frame, you consider the user active.

 

The benefit of using both approaches at the same time is that the status is more accurate if JavaScript is turned on, but it still works if JavaScript is blocked.

Based on what you have said Jacques, would it be correct in assuming that you could use the Ajax heartbeat as an activity status, i.e. online, away, offline.

 

The HTTP request, would be on every single page and would therefore only update if and when some sort of link is clicked on the page?

 

So based on this I would have a table with a structure such as

 

memberID, activity_status, heartbeat, last_active

 

The columns will function as follows:

activity_status - HTTP request timestamp,

heartbeat - Ajax request timestamp,

last_active - the time of the logout request.

 

  • How will it identify if a user goes offline?
  • The HTTP request will only update when a request is sent, but what happens if JavaScript is blocked and the user simply closes the browser?
  • What are your thoughts?
  • Any idea where I could read up on a good tutorial for implementing what you have mentioned?

Thanks

The modern way of doing this stuff is websockets. 

 

I would look into that.  There are also SaaS web sockets server support from companies like https://pusher.com

 

I've used Pusher in the past successfully, although there are others now you might investigate.

The modern way of doing this stuff is websockets. 

 

I would look into that.  There are also SaaS web sockets server support from companies like https://pusher.com

 

I've used Pusher in the past successfully, although there are others now you might investigate.

 

Gizmola, I have two questions:

 

How do I know if my webhost will support websockets to perform this function?

If I decide to run my site as an intranet, how would I gain access to the websockets?

 

I'm looking for a solution that it rather easy to implement and understand, but from what I have read thus far I am left scratching my head...

 

Thanks

By far the easiest approach is Ajax, because that's literally just a few lines of code with no special setup whatsoever.

 

You actually just need one timestamp column and one boolean column to indicate whether the user supports the JavaScript heartbeat feature. The query to select the currently active users would then look like this:

SELECT
    user_id -- or whatever you need
FROM
    users
WHERE
    last_activity >= IF(supports_heartbeat, NOW() - INTERVAL 10 SECOND, NOW() - INTERVAL 15 MINUTE)
;

The HTTP request, would be on every single page and would therefore only update if and when some sort of link is clicked on the page?

 

You do the update in every script whenever you resume an active session. Then you'll cover both page views (GET requests) and actions (POST requests).

 

 

 

How will it identify if a user goes offline?

 

The user is considered offline in one of those three cases:

  • they explicitly log out
  • they support the heartbeat feature but haven't sent a heartbeat within the last 10 seconds
  • they don't support the heartbeat feature and haven't made a request within the last 15 minutes

 

 

 

The HTTP request will only update when a request is sent, but what happens if JavaScript is blocked and the user simply closes the browser?

 

If JavaScript is blocked, the heartbeat feature will simply not be activated. Closing the browser has no effect, because there may still be other tabs/windows, maybe even in a different browser.

 

 

 

Any idea where I could read up on a good tutorial for implementing what you have mentioned?

 

The implementation is very easy and straightforward, so I'd just give it a try.

Thanks again Jacques, I'm going to get cracking on trying to get this to work.

 

I am assuming that with this solution each user would only appear in the activity table once?

 

Once I have the heartbeat up and running, the next thing I want to achieve, is to use this as a tracking system to see when a user logs in / out on a daily basis and use it as a register. Would you recommend using the same table to track this information? Or should I then look at doing this in a completely separate table? I was thinking of possibly creating a view and updating it daily with the first login event of a day, and last logout event.

I have managed to find a bit of code which shows the activity status to the user who is logged in. If I navigate to a different tab, it picks up that I have navigated away, and sets my status as such, the question now is,

 

How do I pass this information to the database so that I can use it to show other users who is online?

How would I show an online icon (small green / yellow / red) instead of echoing a new row of text? 

 

I am using the following code as an include file (I think it is important to state that this is not my own code, so I am not entirely sure how it functions):

<html>
	<head>
		<title>Idle.Js Test</title>
		<script src="../js/jquery.min.js"></script>
		<script src="../js/idle.js"></script>
		<script>
			$(function() {
				function setMessage(msg) {
					$('#ActivityList').append("<li>" + new Date().toTimeString() + ": " + msg + "</li>");
				}

				var awayCallback = function() {
					setMessage("away");
				};
				var awayBackCallback = function() {
					setMessage("back");
				};
				var hiddenCallback = function() {
					setMessage("User is not looking at page");
				};
				var visibleCallback = function(){
					setMessage("User started looking at page again")
				};
				
				var idle = new Idle({
					onHidden : hiddenCallback,
					onVisible : visibleCallback,
					onAway : awayCallback,
					onAwayBack : awayBackCallback,
					awayTimeout : $('#Timeout').val() //away with default value of the textbox
				}).start();

				$('#Timeout').keydown(function(e) {
					if(e.keyCode == 13) {

						var timeout = $(this).val();
						setMessage("Timeout changed to: " + timeout);
						idle.setAwayTimeout(timeout);
					}
				})
			});

		</script>
		<style>
			#ActivityList {
				border: 1px solid #ccc;
				padding: 5px;
				width: 400px;
			}
			#ActivityList li {
				border-bottom: 1px solid #eee;
				margin: 5px 0;
			}
		</style>
	</head>
	<body>
		<label for="Timeout">Set timeout</label>
		<input id="Timeout" type="text" value="10000" />
		<ul id="ActivityList"></ul>
	</body>
</html>

Idle.js file:


(function() {
  var Idle;

  if (!document.addEventListener) {
    if (document.attachEvent) {
      document.addEventListener = function(event, callback, useCapture) {
        return document.attachEvent("on" + event, callback, useCapture);
      };
    } else {
      document.addEventListener = function() {
        return {};
      };
    }
  }

  if (!document.removeEventListener) {
    if (document.detachEvent) {
      document.removeEventListener = function(event, callback) {
        return document.detachEvent("on" + event, callback);
      };
    } else {
      document.removeEventListener = function() {
        return {};
      };
    }
  }

  "use strict";

  Idle = {};

  Idle = (function() {
    Idle.isAway = false;

    Idle.awayTimeout = 3000;

    Idle.awayTimestamp = 0;

    Idle.awayTimer = null;

    Idle.onAway = null;

    Idle.onAwayBack = null;

    Idle.onVisible = null;

    Idle.onHidden = null;

    function Idle(options) {
      var activeMethod, activity;
      if (options) {
        this.awayTimeout = parseInt(options.awayTimeout, 10);
        this.onAway = options.onAway;
        this.onAwayBack = options.onAwayBack;
        this.onVisible = options.onVisible;
        this.onHidden = options.onHidden;
      }
      activity = this;
      activeMethod = function() {
        return activity.onActive();
      };
      window.onclick = activeMethod;
      window.onmousemove = activeMethod;
      window.onmouseenter = activeMethod;
      window.onkeydown = activeMethod;
      window.onscroll = activeMethod;
      window.onmousewheel = activeMethod;
    }

    Idle.prototype.onActive = function() {
      this.awayTimestamp = new Date().getTime() + this.awayTimeout;
      if (this.isAway) {
        if (this.onAwayBack) {
          this.onAwayBack();
        }
        this.start();
      }
      this.isAway = false;
      return true;
    };

    Idle.prototype.start = function() {
      var activity;
      if (!this.listener) {
        this.listener = (function() {
          return activity.handleVisibilityChange();
        });
        document.addEventListener("visibilitychange", this.listener, false);
        document.addEventListener("webkitvisibilitychange", this.listener, false);
        document.addEventListener("msvisibilitychange", this.listener, false);
      }
      this.awayTimestamp = new Date().getTime() + this.awayTimeout;
      if (this.awayTimer !== null) {
        clearTimeout(this.awayTimer);
      }
      activity = this;
      this.awayTimer = setTimeout((function() {
        return activity.checkAway();
      }), this.awayTimeout + 100);
      return this;
    };

    Idle.prototype.stop = function() {
      if (this.awayTimer !== null) {
        clearTimeout(this.awayTimer);
      }
      if (this.listener !== null) {
        document.removeEventListener("visibilitychange", this.listener);
        document.removeEventListener("webkitvisibilitychange", this.listener);
        document.removeEventListener("msvisibilitychange", this.listener);
        this.listener = null;
      }
      return this;
    };

    Idle.prototype.setAwayTimeout = function(ms) {
      this.awayTimeout = parseInt(ms, 10);
      return this;
    };

    Idle.prototype.checkAway = function() {
      var activity, t;
      t = new Date().getTime();
      if (t < this.awayTimestamp) {
        this.isAway = false;
        activity = this;
        this.awayTimer = setTimeout((function() {
          return activity.checkAway();
        }), this.awayTimestamp - t + 100);
        return;
      }
      if (this.awayTimer !== null) {
        clearTimeout(this.awayTimer);
      }
      this.isAway = true;
      if (this.onAway) {
        return this.onAway();
      }
    };

    Idle.prototype.handleVisibilityChange = function() {
      if (document.hidden || document.msHidden || document.webkitHidden) {
        if (this.onHidden) {
          return this.onHidden();
        }
      } else {
        if (this.onVisible) {
          return this.onVisible();
        }
      }
    };

    return Idle;

  })();

  if (typeof define === 'function' && define.amd) {
    define([], Idle);
  } else if (typeof exports === 'object') {
    module.exports = Idle;
  } else {
    window.Idle = Idle;
  }

}).call(this);

 

Edited by SalientAnimal

Web sockets is the evolution of ajax requests (and Long Polling), and has features that improve on things in ajax that are not ideal for persistent connections.  

 

There are useful things available in Websockets that support pub/sub which for something like a user list is really nice.

 

In all cases the client side technology is javascript.

 

On the server side, there are various ways to implement a web socket server, although web sockets have largely grown up along with Node.js and frequently people who roll their own solution do so with Node.

 

A way around the entire requirement for a separate web socket server process is to use Pusher, which acts as a proxy for you.  It accepts the web socket connections on your behalf from your clients and makes calls to your server to get the actual information needed to service the connection.  They have lots of code example and it's not a bad idea to try them out to get your feet wet with web sockets.

Gizmola, I spent the entire day today searching for both an Ajax and Websocket solution. 

 

I would very much like to implement the websocket solution, but I was not able to find a working solution with code that I could easily implement into my page. If you could direct me to a good working example which would help me in achieving my goals, I would really appreciate that.

 

Some more info about the site that I am developing is:

 

When a user has been registered, they will be assigned to a team / group, when the group leader signs in they will need to see only their team members who are both online and offline. The online members should have a little green light next to their names and the offline members should have a red light. Members who are in an away state should reflect an orange light.

 

For this reason, I will need to be looking up certain information in my database, like which team they belong to and return this id along with the users status to the group leader.

 

Regular users should be able to see all members status.

I forgot to mention that I did also have a look at Pusher last night, but I am left horribly confused trying to put things together. 

 

The Pusher option is really nice for when the site runs completely online, however there may be instances where my entire site is actually going to run on a intranet which won't have access to and internet connection.

Hi,

 

  Since Pusher isn't an option for you, here is an option to look at that will allow you to keep your server side codebase in PHP:  http://socketo.me

 

They have a simple proof of concept/tutorial.  I'd recommend following those steps and get everything installed and tested, and from there it would just be a matter of adding your specific application logic.

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.