CMS  Version 3.9
activity_tracker.inc
Go to the documentation of this file.
1 <?php
7 Fakoli::using("settings");
8 
9 class ActivityTracker
10 {
11  static function registerActivityTracker()
12  {
13  trace("Registering Redirect Log Callback", 1);
14  registerRedirectLogCallback(array(ActivityTracker, logActivity));
15  }
16 
17  static function logActivity()
18  {
19  global $user;
20  //if (!$user) return;
21 
22  $enabled = Settings::getValue("activity_tracker", "track_activity");
23  $entryPoint = basename($_SERVER['SCRIPT_NAME']);
24 
25  if (!$enabled || ($entryPoint != 'page.php' && $entryPoint != 'action.php')) return;
26 
27  $activity = new UserActivity();
28 
29  $activity->user_id = is_object($user) ? $user->get($user->primary_key) : 0;
30 
31  $response_time = number_format(microtime(true) - Fakoli::$timestamp, 3);
32  trace("Request completed in {$response_time}s", 3);
34  $activity->method = $_SERVER['REQUEST_METHOD'];
35  $activity->referer = $_SERVER['HTTP_REFERER'];
36  $activity->session_id = session_id();
37  $activity->activity_time = Fakoli::$requestStartTime;
38  $activity->response_time = $response_time;
39  $activity->memory_usage = memory_get_peak_usage();
40 
41  $start = $_SESSION["ActivityTracker::session_start"];
42  if (!$start)
43  {
44  $start = date("Y-m-d");
45  $_SESSION["ActivityTracker::session_start"] = $start;
46  }
47 
48  $log = ActivityTracker::getLogFile(session_id(), $start);
49 
50  $sessionLogID = $_SESSION["ActivityTracker::tracking"];
51 
52  if ($sessionLogID)
53  {
54  // Update the activity session record stats atomically
55  $sessionLog = new UserActivitySession();
56  $sessionLog->session_id = $sessionLogID;
57 
58  $update = "SET user_id=".$activity->user_id.", session_end = ".$activity->quoteFieldValue('activity_time', DateTime).", request_count = request_count + 1";
59  if (startsWith($activity->uri, "/action/activity_tracker/save"))
60  {
61  $update .= ", feedback_count = feedback_count + 1";
62  }
63 
64  if (startsWith($activity->uri, "/action/"))
65  {
66  $update .= ", action_count = action_count + 1";
67  }
68  else
69  {
70  $update .= ", page_views = page_views + 1";
71  }
72 
73  $sessionLog->updateExplicit($update);
74  }
75  else
76  {
77  // Create the activity session record
78  $sessionLog = new UserActivitySession();
79  if (!$sessionLog->tableExists()) return;
80 
81  $sessionLog->session_identifier = session_id();
82  $sessionLog->user_id = $activity->user_id;
83  $sessionLog->session_start = $activity->activity_time;
84  $sessionLog->session_end = $activity->activity_time;
85  $sessionLog->request_count = 1;
86  $sessionLog->action_count = (startsWith($activity->uri, "/action/")) ? 1 : 0;
87  $sessionLog->page_views = (startsWith($activity->uri, "/action/")) ? 0 : 1;
88  $sessionLog->ip_address = $_SERVER["REMOTE_ADDR"];
89  $sessionLog->user_agent = $_SERVER["HTTP_USER_AGENT"];
90  $sessionLog->save();
91 
92  $_SESSION["ActivityTracker::tracking"] = $sessionLog->session_id;
93  }
94 
95  error_log($activity->toJSON()."\n", 3, $log);
96  }
97 
98  static function getLogFile($session_identifier, $start)
99  {
100  $dir = Settings::getValue("activity_tracker", "logging_directory").DIRECTORY_SEPARATOR.$start;
101  if (!file_exists($dir)) mkdir($dir);
102 
103  return $dir.DIRECTORY_SEPARATOR.$session_identifier.".json";
104  }
105 
106  static function formatMemoryUsage($activity)
107  {
108  return getScaledSize($activity->memory_usage, 2);
109  }
110 
118  static function deleteUser($user)
119  {
120  $pk = $user->getPrimaryKey();
121 
122  $user_id = $user->$pk;
123  trace("Component activity_tracker is deleting objects dependent on user_id {$user_id}", 3);
124 
125  $tx = new DataTransaction();
126 
127  try
128  {
129  $feedback = new UserFeedback();
130  $feedback->joinTransaction($tx);
131  $feedback->delete("WHERE user_id={$user_id}");
132 
133  $userActivity = new UserActivity();
134  $userActivity->joinTransaction($tx);
135  $userActivity->delete("WHERE user_id={$user_id}");
136 
137  $tx->commit();
138  }
139  catch(Exception $e)
140  {
141  $tx->rollback();
142  throw $e;
143  }
144  return $user;
145  }
146 
147  static function setDefaults()
148  {
149  global $config;
150 
151  $base = $config['uploadbase'].DIRECTORY_SEPARATOR."user_activity_logs";
152 
153  Settings::setDefaultValue("activity_tracker", "track_activity", "", Boolean, "Track user activity. Use this for usage statistics and troubleshooting. For better performance, disable this on sites with heavy traffic.", "Activity Tracking");
154  Settings::setDefaultValue("activity_tracker", "enable_feedback", "", Boolean, "Enable the user feedback feature. You will also need to assign the feedback module to a position in your template.", "User Feedback", null, 1);
155  Settings::setDefaultValue("activity_tracker", "feedback_title", "Provide Feedback", String, "Title text for the user feedback module", "User Feedback", null, 3);
156  Settings::setDefaultValue("activity_tracker", "require_login", false, Boolean, "Whether the feedback feature is available to guest users (those who haven't yet logged in)", "User Feedback", null, 2);
157  Settings::setDefaultValue("activity_tracker", "confirmation_message", "Thank you, your feedback is appreciated!", String, "The message to display after the user submits feedback", "User Feedback", null, 4);
158 
159  Settings::setDefaultValue("activity_tracker", "send_email", false, Boolean, "Send a notification email when feedback is submitted. If you set this to true, configure recipient and message via the 'feedback_notification' email template", "User Feedback", null, 5);
160 
161  Settings::setDefaultValue("activity_tracker", "google_analytics_tracking_id", "", String, "Leave blank if tracking is not desired.", "Google Analytics");
162  Settings::setDefaultValue("activity_tracker", "google_analytics_version", "Classic", String, "Specifies the API Version of Google Analytics to use. Select this based on your site's analytics configuration on google.com", "Google Analytics", "Classic\nUniversal");
163 
164  Settings::setDefaultValue("activity_tracker", "logging_directory", $base, String, "Specifies the directory to which user activity logs will be written", "Activity Tracking");
165 
166  ActivityTracker::createLogDirectory();
167  }
168 
169  static function isEnabled()
170  {
171  return Settings::getValue("activity_tracker", "track_activity");
172  }
173 
174  static function createLogDirectory()
175  {
176  if (ActivityTracker::isEnabled())
177  {
178  $logDirectory = Settings::getValue("activity_tracker", "logging_directory");
179 
180  if (!file_exists($logDirectory))
181  {
182  mkdir($logDirectory);
183  }
184  }
185  }
186 
187  static function upgradeComponent($version)
188  {
190  $mgr->upgrade($version);
191  }
192 
197  static function getGoogleAnalyticsScript($id)
198  {
199  $trackingMode = Settings::getValue("activity_tracker", "google_analytics_version");
200 
201  $script = "";
202 
203  ob_start();
204 
205  if ($trackingMode == "Classic")
206  {
207 ?>
208  <script type="text/javascript">
209 
210  var _gaq = _gaq || [];
211  _gaq.push(['_setAccount', '<?php echo $id ?>']);
212  _gaq.push(['_trackPageview']);
213 
214  (function()
215  {
216  var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
217  ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
218  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
219  })();
220 
221 </script>
222 <?php
223  }
224  else if ($trackingMode == "Universal")
225  {
226 ?>
227 <script type="text/javascript">
228 
229  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
230  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
231  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
232  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
233 
234  ga('create', '<?php echo $id ?>', 'auto');
235  ga('send', 'pageview');
236 
237 </script>
238 <?
239  }
240 
241  $script .= ob_get_contents();
242  ob_end_clean();
243 
244  return $script;
245  }
246 
255  static function postProcessContent($content)
256  {
257  global $script;
258 
259  $id = Settings::getValue("activity_tracker", "google_analytics_tracking_id");
260 
261  if($id)
262  {
263  $script .= ActivityTracker::getGoogleAnalyticsScript($id);
264  }
265 
266  return $content;
267  }
268 
269 
270  static function getBrowser($session)
271  {
272  $browser = get_browser($session->user_agent);
273  return $browser ? "{$browser->parent} ({$browser->platform})" : "N/A";
274  }
275 
281  static function loadSession($session)
282  {
283  $log = ActivityTracker::getLogFile($session->session_identifier, date("Y-m-d", strtotime($session->session_start)));
284 
285  trace("Opening session log $log", 3);
286 
287  $fp = fopen($log, "r");
288 
289  $records = array();
290 
291  while($json = fgets($fp))
292  {
293  $activity = new UserActivity();
294  $activity->fromJSON($json);
295  $records[] = $activity;
296  }
297 
298  fclose($fp);
299  return $records;
300  }
301 
305  static function migrateData($session)
306  {
307  $records = Query::create(UserActivity, "WHERE user_id=:u AND session_id=:s ORDER BY activity_id")
308  ->bind(":u", $session->user_id, ":s", $session->session_id)
309  ->filter(new ExclusionFilter("response_time")) // Not captured in previous version
310  ->execute();
311 
312  $start = date("Y-m-d", strtotime($session->session_start));
313 
314  $log = ActivityTracker::getLogFile($session->session_id, $start);
315 
316  if (file_exists($log)) return;
317 
318  $userSession = new UserActivitySession();
319  $userSession->session_identifier = $session->session_id;
320  $userSession->user_id = $session->user_id;
321  $userSession->session_start = $session->session_start;
322  $userSession->session_end = $session->session_end;
323  $userSession->feedback_count = $session->num_feedback;
324 
325  $pageViews = 0;
326  $actionCount = 0;
327  $requestCount = 0;
328 
329  foreach($records as $record)
330  {
331  error_log($record->toJSON()."\n", 3, $log);
332 
333  $requestCount++;
334  if (startsWith("/action", $record->uri))
335  {
336  $actionCount++;
337  }
338  else
339  {
340  $pageViews++;
341  }
342  }
343 
344  $userSession->page_views = $pageViews;
345  $userSession->action_count = $actionCount;
346  $userSession->requestCount = $requestCount;
347  $userSession->save();
348  }
349 }
350 
351 class ActivityReportFilterHelper
352 {
353  function __construct()
354  {
355 
356  }
357 
358  function setup()
359  {
360  $session = new UserActivitySession();
361  $session->filter = new InclusionFilter("session_start", "feedback_user");
362  $session->overrideFieldType("feedback_user", Boolean);
363  $session->fromGET();
364 
365  $filterForm = new FilterForm($session);
366  $filterForm->hide("session_end");
367  $dateRangeFilter = new DateRangeFilterRenderer($filterForm, "session_start", "session_end", "month");
368  $userCheckBox = new BooleanFilterFieldRenderer($filterForm, "feedback_user", "Show only users that provided feedback", 1);
369  $filterForm->setHandler("feedback_user", array($this, getFeedbackUserClause));
370 
371  return $filterForm;
372  }
373 
381  static function getFeedbackUserClause($name, $value)
382  {
383  if(!$value) return;
384  return "feedback_count > 0";
385  }
386 }
387 
388 class SessionDetailsHelper
389 {
390  var $showImages = false;
391 
392  function __construct()
393  {
394  $this->showImages = checkNumeric($_GET["showImages"]);
395  }
396 
397  function filterImages($record)
398  {
399  if (!$this->showImages && startsWith($record->uri, "/action/image/")) return false;
400  return "";
401  }
402 }?>
$filterForm
$user_id
$feedback
Definition: feedback.inc:7
$dateRangeFilter
$showImages
if(! $showImages) if($session_id) else if($sessionIdentifier) $activity
$dir
Definition: delete.inc:44
$base
Definition: delete.inc:45
$name
Definition: upload.inc:54
static $requestStartTime
Start of request in DateTime format.
Definition: core.inc:87
static $timestamp
Microsecond Timestamp for start of request.
Definition: core.inc:86
static $requestURI
The initial request URI.
Definition: core.inc:88
static using()
Import the datamodels, views and manifest for the specified component(s).
Definition: core.inc:116
static getValue($component, $name)
Retrieve the value of the specified Setting.
Definition: settings.inc:104
static setDefaultValue($component, $name, $value, $field_type="String", $annotation="", $category="", $options="", $weight=0)
Sets the default value of the given component setting.
Definition: settings.inc:174
global $user
global $config
Definition: import.inc:4
$_SESSION["useMobile"]
Definition: override.inc:7
$enabled
Definition: save.inc:4
if(array_key_exists("HTTP_IF_MODIFIED_SINCE", $_SERVER)) $content
Definition: styles.css.inc:24