{"id":2714,"date":"2022-07-05T17:29:06","date_gmt":"2022-07-05T22:29:06","guid":{"rendered":"https:\/\/xlinesoft.com\/blog\/?p=2714"},"modified":"2022-10-13T16:53:41","modified_gmt":"2022-10-13T21:53:41","slug":"tracking-visitors-behaviour-in-your-web-application","status":"publish","type":"post","link":"https:\/\/xlinesoft.com\/blog\/2022\/07\/05\/tracking-visitors-behaviour-in-your-web-application\/","title":{"rendered":"Tracking visitors behaviour in your web application"},"content":{"rendered":"<p>So you want to know how much time users spend on any specific page of your web application? This article explains how to log what pages your users visit and how much time they spend on each page. This kind of data can provide valuable insight into what forms of your application are too complicated and need to be split into several smaller forms. Or if they keep coming back to the welcome page this may mean your navigation inside the app can be improved. <\/p>\n<p><a href=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2022\/07\/timetracker.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2022\/07\/timetracker-600x306.png\" alt=\"\" width=\"600\" height=\"306\" class=\"alignnone size-medium wp-image-2728\" srcset=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2022\/07\/timetracker-600x306.png 600w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2022\/07\/timetracker-768x391.png 768w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2022\/07\/timetracker-1024x522.png 1024w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2022\/07\/timetracker.png 1282w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/a><br \/>\n<!--more--><\/p>\n<h4>1. Create log table<\/h4>\n<p>The following SQL script creates the log table in the database. The syntax is for MySQL, you can create a similar table in any other database manually, just make sure that field names and datatypes stay the same.<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"sql\" name=\"mshighlighter\" >\r\nCREATE TABLE `timetracker` (\r\n  `trackerId` int(11) NOT NULL AUTO_INCREMENT,\r\n  `pagename` varchar(250)  DEFAULT NULL, \/* page URL *\/\r\n  `timeon` datetime DEFAULT NULL, \/* when user entered the page *\/\r\n  `timeoff` datetime DEFAULT NULL, \/* when user left the page *\/\r\n  `userID` varchar(100) DEFAULT NULL, \/* username *\/\r\n  `recordID` varchar(100) DEFAULT NULL, \/* in the case of edit\/view pages this field will store the ID of the record *\/\r\n  PRIMARY KEY (`trackerId`)\r\n)<\/textarea><\/pre>\n<\/div>\n<p>A few notes here:<\/p>\n<p><strong>Note 1:<\/strong> Login is not required. If your project doesn&#8217;t use login, &#8216;userID&#8217; field will be empty. <\/p>\n<p><strong>Note 2:<\/strong> if &#8216;timeoff&#8217; field is empty that means user spent less than five seconds on the page.<\/p>\n<p><strong>Note 3:<\/strong> it makes sense to add &#8216;timetracker&#8217; table to the project as well so you can test this functionality. If you do so, set &#8216;View as&#8217; time of timeon\/timeoff fields to &#8216;Datetime&#8217;.  <\/p>\n<h4>2. Javascript code<\/h4>\n<p>The following Javascript code needs to be added under Event Editor -> custom_functions.js<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"js\" name=\"mshighlighter\" >\r\n$(\"document\").ready(function() {\r\n    Runner.customEvents = [];\r\n    \/\/ every notifyInterval seconds we execute an AJAX requests that tells the server that user is still ont he page\r\n    var notifyInterval = 5;\r\n\r\n    \/\/ this function is executed on every page load, here we tell the server what page the user currently on\r\n\r\n    function setPageTimer(pageObj) {\r\n\t\/\/ ajax- parameters with the page URL\r\n        var notify_params = { pageOpen: 1, pageName: Runner.pages.getUrl(pageObj.shortTName,pageObj.pageType) };\r\n\t\/\/ if this is an Edit\/View page we also pass an ID of the record\r\n        if (pageObj.pageType === \"edit\" || pageObj.pageType === \"view\") notify_params.recordID = pageObj.keys[0];\r\n\t\/\/ we send AJAX request and get back trackerId value of the current log table recod\r\n        $.get(\"\", notify_params, function(TrackerID) {\r\n\t\/\/ send AJAX request with the current notifyInterval value, that tells the server the user is still on the page\r\n            interval = setInterval(function() {\r\n                $.get(\"\", { TrackerID: TrackerID });\r\n            }, notifyInterval * 1000);\r\n        });\r\n    }\r\n\r\n    var originalInit = Runner.pages.RunnerPage.prototype.init;\r\n  \r\n    Runner.pages.RunnerPage.prototype.init = function() {\r\n        var pageObj = this;\r\n        var isTab = typeof this.tabControl !== \"undefined\";\r\n\t\/\/ check if the current page a details tab\r\n        if (isTab) {\r\n\r\n\tif (!Runner.customEvents.includes(this.tName + \"_\" + this.pageType)) {\r\n            Runner.customEvents.push(this.tName + \"_\" + this.pageType);\r\n            pageObj.on(\"afterPageReady\", function() {\r\n                \/\/ when tab is closed we clear the interval counter\r\n\t\tpageObj.tabControl.off(\"hide.bs.tab\").on(\"hide.bs.tab\", function(e) {\r\n                    clearInterval(interval);\r\n                });\r\n\t\t\/\/ when details tab is open we start the counter\r\n                pageObj.tabControl.off(\"show.bs.tab\").on(\"show.bs.tab\", function(e) {\r\n                    var activeTab = $(e.target);\r\n                    var panelContent = activeTab.parents(\"ul\").next();\r\n                    var activePanel = panelContent.find(\".tab-pane.active\");\r\n\r\n                    setPageTimer(Runner.pages.PageManager.getById(activePanel.find(\".r-form\").attr(\"data-pageid\")));\r\n\r\n                });\r\n            });\r\n\t\t\t\t}\r\n\r\n        }\r\n\r\n        if (!isTab || (isTab && this.$panel.parents(\".tab-pane\").hasClass(\"active\"))) {\r\n            setPageTimer(pageObj);\r\n        }\r\n\r\n        originalInit.call(this);\r\n    }\r\n});<\/textarea><\/pre>\n<\/div>\n<h4>3. Server-side code (PHP and C#)<\/h4>\n<p>The following code goes to the AfterAppInit event.<\/p>\n<p><strong>PHP:<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\n$currentDateTimeForDb = localdatetime2db( runner_date_format(\"m-d-y H:i:s\") );\r\n\/\/ receiving AJAX request with the new page URL\r\n\/\/ in timetracker table we create a new record \r\n\/\/ and return the TrackerID value of the new record\r\nif( postvalue(\"pageOpen\") != false ){\r\n\t\t$data = array();\r\n\t\t$data[\"pagename\"] = postvalue(\"pageName\");\r\n\t\t$data[\"timeon\"] = $currentDateTimeForDb;\r\n\t\t$data[\"userID\"] =  Security::getUserName();\r\n\t\tif(postvalue(\"recordID\") != false){\r\n\t\t\t$data[\"recordID\"] = postvalue(\"recordID\");\r\n\t\t}\r\n\t\tDB::Insert(\"timetracker\", $data);\r\n\t\t\/\/return TrackerID\r\n\t\techo DB::LastId();\r\n\t\texit();\r\n\r\n}\r\n\/\/ receiving AJAX request that tell us we are still on the same page\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442\u0441\u044f\r\n\/\/ we just update the value of timeoff field for the current TrackerID\r\nif( postvalue(\"TrackerID\") !=false ){\r\n\t$now_datetime = $currentDateTimeForDb;\r\n\tDB::Update(\"timetracker\",array(\"timeoff\"=> $now_datetime ),array(\"trackerId\" => postvalue(\"TrackerID\") ));\r\n\texit();\r\n}<\/textarea><\/pre>\n<\/div>\n<p><strong>C#:<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"js\" name=\"mshighlighter\" >\r\ndynamic currentDateTimeForDb = null;\r\ncurrentDateTimeForDb = XVar.Clone(CommonFunctions.localdatetime2db((XVar)(MVCFunctions.runner_date_format(new XVar(\"m-d-y H:i:s\")))));\r\nif(MVCFunctions.postvalue(new XVar(\"pageOpen\")) != false)\r\n{\r\n\tdata = XVar.Clone(XVar.Array());\r\n\tdata.InitAndSetArrayItem(MVCFunctions.postvalue(new XVar(\"pageName\")), \"pagename\");\r\n\tdata.InitAndSetArrayItem(currentDateTimeForDb, \"timeon\");\r\n\tdata.InitAndSetArrayItem(Security.getUserName(), \"userID\");\r\n\tif(MVCFunctions.postvalue(new XVar(\"recordID\")) != false)\r\n\t{\r\n\t\tdata.InitAndSetArrayItem(MVCFunctions.postvalue(new XVar(\"recordID\")), \"recordID\");\r\n\t}\r\n\tDB.Insert(new XVar(\"timetracker\"), (XVar)(data));\r\n\tMVCFunctions.Echo(DB.LastId());\r\n\tMVCFunctions.ob_flush();\r\n\tHttpContext.Current.Response.End();\r\n\tthrow new RunnerInlineOutputException();\r\n}\r\nif(MVCFunctions.postvalue(new XVar(\"TrackerID\")) != false)\r\n{\r\n\tdynamic now_datetime = null;\r\n\tnow_datetime = XVar.Clone(currentDateTimeForDb);\r\n\tDB.Update(new XVar(\"timetracker\"), (XVar)(new XVar(\"timeoff\", now_datetime)), (XVar)(new XVar(\"trackerId\", MVCFunctions.postvalue(new XVar(\"TrackerID\")))));\r\n\tMVCFunctions.ob_flush();\r\n\tHttpContext.Current.Response.End();\r\n\tthrow new RunnerInlineOutputException();\r\n}\r\nreturn null;<\/textarea><\/pre>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>So you want to know how much time users spend on any specific page of your web application? This article explains how to log what pages your users visit and how much time they spend on each page. This kind of data can provide valuable insight into what forms of your application are too complicated and need to be split into several smaller forms. Or if they keep coming back to the welcome page this may mean your navigation inside the app can be improved.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16,1,8],"tags":[],"_links":{"self":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/2714"}],"collection":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/comments?post=2714"}],"version-history":[{"count":15,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/2714\/revisions"}],"predecessor-version":[{"id":2823,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/2714\/revisions\/2823"}],"wp:attachment":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/media?parent=2714"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/categories?post=2714"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/tags?post=2714"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}