Projet

Général

Profil

Paste
Télécharger (29,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / user_stats / user_stats.module @ 87dbc3bf

1
<?php
2
 
3
/**
4
 * @file
5
 * User Stats provides commonly requested user statistics for themers.
6
 * These are:
7
 *  - days registered;
8
 *  - join date;
9
 *  - days since last login;
10
 *  - days since last post;
11
 *  - post count;
12
 *  - login count;
13
 *  - user online/offline;
14
 *  - IP address;
15
 *
16
 * Note for hackers: function parameters should go in the order
17
 * $op/$type, $uid, $data (where applicable).
18
 */
19

    
20
/**
21
 * Implements hook_permission().
22
 */
23
function user_stats_permission() {
24
  return array(
25
    'administer user stats' => array(
26
      'title' => t('administer user stats'),
27
      'description' => t('TODO Add a description for \'administer user stats\''),
28
    ),
29
    'View statistics' => array(
30
      'title' => t('View statistics'),
31
      'description' => t('TODO Add a description for \'View statistics\''),
32
    ),
33
    'View IP addresses' => array(
34
      'title' => t('View IP addresses'),
35
      'description' => t('TODO Add a description for \'View IP addresses\''),
36
    ),
37
  );
38
}
39

    
40
/**
41
 * Implements hook_menu().
42
 */
43
function user_stats_menu() {
44
  $items = array();
45

    
46
  // Admin settings
47
  $items['admin/config/people/user_stats'] = array(
48
    'title' => 'User Stats settings',
49
    'description' => 'Configuration of user stats module options.',
50
    'page callback' => 'drupal_get_form',
51
    'page arguments' => array('user_stats_admin_settings'),
52
    'access arguments' => array('administer user stats'),
53
    'file' => 'user_stats.admin.inc',
54
    'type' => MENU_NORMAL_ITEM,
55
  );
56
  $items['admin/config/people/user_stats/reset_post_count'] = array(
57
    'title' => 'reset user post stats',
58
    'page callback' => 'user_stats_reset_post_count',
59
    'access arguments' => array('administer user stats'),
60
    'file' => 'user_stats.admin.inc',
61
    'type' => MENU_CALLBACK,
62
  );
63
  $items['admin/config/people/reset_login_count'] = array(
64
    'title' => 'reset user login stats',
65
    'page callback' => 'user_stats_reset_login_count',
66
    'access arguments' => array('administer user stats'),
67
    'file' => 'user_stats.admin.inc',
68
    'type' => MENU_CALLBACK,
69
  );
70

    
71
  return $items;
72
}
73

    
74
/**
75
 * Returns user stats.
76
 *
77
 * @param $type
78
 *   The statistic to return. Possible values are:
79
 *   - "ip_address"
80
 *   - "join_date"
81
 *   - "login_count"
82
 *   - "login_days"
83
 *   - "post_count"
84
 *   - "post_days"
85
 *   - "reg_days"
86
 *   - "online"
87
 *   - "profile"
88
 * @param $uid
89
 *   The user id who's stats should be retrieved.
90
 *
91
 * @return
92
 *   The statistic requested. Every statistic except join_date, online and IP address is a numeric.
93
 *   Join date is a string, whilst online is a boolean and IP Address a string.
94
 *   Note: if $type = "post_days" and the user hasn't posted any content (of the
95
 *   counted types) then 'n/a' is returned.
96
 */
97
function user_stats_get_stats($type, $uid) {
98
  // Sometimes $uid can be NULL (comment previews for example).
99
  if (!is_numeric($uid)) {
100
    return;
101
  }
102

    
103
  // IP address is really a bit of feature creep.
104
  // At some point in the future, this could be split off into its own module.
105
  if ($type == 'ip_address') {
106
    if (!user_access('View IP addresses')) {
107
      return FALSE;
108
    }
109
    // Check cache.
110
    if (user_stats_cache_get($type, $uid) === FALSE) {
111
      $query = db_query("SELECT ip_address
112
        FROM {user_stats_ips} WHERE uid = :uid
113
        ORDER BY first_seen_timestamp LIMIT 1", array(':uid' => $uid));
114
      user_stats_cache_set($type, $uid, $query->fetchField());
115
    }
116
    return user_stats_cache_get($type, $uid);
117
  }
118

    
119
  // Everything else is under the 'View statistics' permission.
120
  if (!user_access('View statistics')) {
121
    return FALSE;
122
  }
123

    
124
  // Check cache first.
125
  if (user_stats_cache_get($type, $uid) !== FALSE) {
126
    return user_stats_cache_get($type, $uid);
127
  }
128

    
129
  switch ($type) {
130
    case 'join_date':
131
      $data = db_query("SELECT created FROM {users} WHERE uid = :uid", array(':uid' => $uid))->fetchField();
132
      break;
133
    case 'login_count':
134
      if (!variable_get('user_stats_count_logins', TRUE)) {
135
        $data = 'n/a';
136
      }
137
      else if (user_stats_isset($type, $uid)) {
138
        $data = db_query("SELECT value FROM {user_stats_values} WHERE name = :name AND uid = :uid", array(':name' => 'login_count', ':uid' => $uid))->fetchField();
139
      }
140
      else {
141
        return 0;
142
      }
143
      break;
144
    case 'login_days':
145
      $user_access = db_query("SELECT access FROM {users} WHERE uid = :uid", array(':uid' => $uid))->fetchField();
146
      $data = floor((REQUEST_TIME - $user_access) / 86400);
147
      break;   
148
    case 'login_date':
149
      $data = db_query("SELECT access FROM {users} WHERE uid = :uid", array(':uid' => $uid))->fetchField();
150
      break;
151
    case 'post_count':
152
      if (!variable_get('user_stats_count_posts', TRUE) && !variable_get('user_stats_count_comments', TRUE)) {
153
        $data = 'n/a';
154
      }
155
      else if (!user_stats_isset('post_count', $uid)) {
156
        user_stats_post_count_update('reset', $uid);
157
      }
158
      $query = db_query("SELECT value FROM {user_stats_values}
159
        WHERE name = :name AND uid = :uid", array(':name' => 'post_count', ':uid' => $uid));
160
      $posts = $query->fetchField();
161

    
162
      //@TODO Figure out why adding comments here wasn't in the D6 version 
163
      if(variable_get('user_stats_count_comments', TRUE)){
164
        
165
      }
166
      $data = $posts;
167

    
168
      break;
169
    case 'post_days':
170
      $last_post = _user_stats_last_post($uid);
171
      if ($last_post !== FALSE) {
172
        $data = floor((REQUEST_TIME - $last_post) / 86400);
173
      }
174
      else {
175
        $data = 'n/a';
176
      }
177
      break;
178
    case 'reg_days':
179
      $user_created = db_query("SELECT created FROM {users} WHERE uid = :uid", array(':uid' => $uid))->fetchField();
180
      $data = floor((REQUEST_TIME - $user_created) / 86400);
181
      break;
182
    case 'online':
183
      $user_access = db_query("SELECT timestamp FROM {sessions} WHERE uid = :uid", array(':uid' => $uid))->fetchField();
184
      $data = ((REQUEST_TIME - $user_access) < variable_get('user_block_seconds_online', 900) ? TRUE : FALSE);
185
      break;
186
    default:
187
      // Check for custom statistics
188
      $custom_stats = array();
189
      $module_list = module_implements('default_user_stats');
190
      foreach ($module_list as $module) {
191
        $custom_stats = array_merge($custom_stats, module_invoke($module, 'default_user_stats'));
192
      }
193

    
194
      if (array_key_exists($type, $custom_stats)) {
195
        module_load_include('module', 'qna');
196
        $data = call_user_func($custom_stats[$type], $uid);
197
        break;
198
      }
199
      else {
200
        // Raise an error if the statistic doesn't exist.
201
        $err_message = 'Statistic "' . check_plain($type) . '" does not exist.';
202
        trigger_error($err_message, E_USER_WARNING);
203
        return;
204
      }
205
  }
206
  user_stats_cache_set($type, $uid, $data);
207
  return user_stats_cache_get($type, $uid);
208
}
209

    
210
/**
211
 * Return data from the non-persistent User Stats cache. Single values
212
 * are returned according to type of statistic and unique user id.
213
 *
214
 * @param $type
215
 *   The type of statistic to retrieve, this corresponds to the statistic
216
 *   types used by user_stats_get_stats().
217
 * @param $uid
218
 *   Unique ID of the user who's statistic is being retrieved.
219
 *
220
 * @return
221
 *   A single value, representing the statistic $type where the unique user id
222
 *   is $uid. Or FALSE if there is no value in the cache for this combination
223
 *   of $type and $uid.
224
 *
225
 * @see user_stats_get_stats().
226
 * @see user_stats_cache_set().
227
 */
228
function user_stats_cache_get($type, $uid) {
229
  $user_stats_cache = user_stats_cache_set();
230
  if (isset($user_stats_cache[$uid][$type])) {
231
    return $user_stats_cache[$uid][$type];
232
  }
233
  else {
234
    return FALSE;
235
  }
236
}
237

    
238
/**
239
 * Store a value in the non-persistent User Stats cache.
240
 *
241
 * If the function is called with no arguments, the entire cache is returned
242
 * without being cleared.
243
 *
244
 * The User Stats cache is a static array, which is why we call it
245
 * non-persistent. The array structure is:
246
 * $user_stats_cache[$uid][$type] = $value.
247
 *
248
 * @param $type
249
 *   The type of statistic being stored, this corresponds to the statistic
250
 *   types used by user_stats_get_stats(), and one extra used to reset the
251
 *   cache: 'reset'.
252
 * @param $uid
253
 *   Unique ID of the user who's statistic is being stored. If the type
254
 *   is set to 'reset', this user id will have the cache values associated with
255
 *   it reset. Alternatively, if $type is set to 'reset' and this is -1, the
256
 *   entire cache will be reset.
257
 *
258
 * @return
259
 *   Array of the entire cache, or NULL if the cache has been reset.
260
 *
261
 * @see user_stats_get_stats().
262
 * @see user_stats_cache_get().
263
 */
264
function user_stats_cache_set($type = NULL, $uid = 0, $data = NULL) {
265
  static $user_stats_cache = array();
266
  // Flush entire cache.
267
  if ($uid == -1 && $type == 'reset') {
268
    unset($user_stats_cache);
269
    return;
270
  }
271
  else if ($uid > -1 && $type == 'reset') {
272
    unset($user_stats_cache[$uid]);
273
    return;
274
  }
275
  // Set cache data. Check against NULL since a zero (in $data at least)
276
  // is valid.
277
  if ($type !== NULL && $data !== NULL) {
278
    $user_stats_cache[$uid][$type] = $data;
279
  }
280

    
281
  return $user_stats_cache;
282
}
283

    
284
/**
285
 * Drupal hook implementations.
286
 */
287

    
288
/**
289
 * Implements hook_node_insert().
290
 */
291
function user_stats_node_insert($node) {
292
  $post_count_content_types = variable_get('user_stats_included_content_types', array());
293
  if ((empty($post_count_content_types) ||
294
    in_array($node->type, $post_count_content_types)) &&
295
      variable_get('user_stats_count_posts', TRUE)) {
296
    if ($node->status) {
297
      user_stats_post_count_update('increment', $node->uid);
298
    }
299
  }
300
  // Do IP Address update.
301
  global $user;
302
  // User IP addresses are only interesting if they are posting the content.
303
  if ($node->uid == $user->uid) {
304
    user_stats_ip_address_update($user->uid, ip_address());
305
  }
306
}
307

    
308
/**
309
 * Implements hook_node_update().
310
 */
311
function user_stats_node_update($node) {
312
  $post_count_content_types = variable_get('user_stats_included_content_types', array());
313
  if ((empty($post_count_content_types) ||
314
    in_array($node->type, $post_count_content_types)) &&
315
      variable_get('user_stats_count_posts', TRUE)) {
316

    
317
    // Can't think of any other way of doing this than resetting the user.
318
    user_stats_post_count_update('reset', $node->uid);
319
  }
320

    
321
  // User IP addresses are only interesting if they are posting the content.
322
  global $user;
323
  if ($node->uid == $user->uid) {
324
    user_stats_ip_address_update($user->uid, ip_address());
325
  }
326
}
327

    
328
/**
329
 * Implements hook_node_delete().
330
 */
331
function user_stats_node_delete($node) {
332
  $post_count_content_types = variable_get('user_stats_included_content_types', array());
333
  if ((empty($post_count_content_types) ||
334
    in_array($node->type, $post_count_content_types)) &&
335
      variable_get('user_stats_count_posts', TRUE)) {
336
    // Node must be published as unpublished nodes would have already been
337
    // removed from user's post count.
338
    if ($node->status) {
339
      user_stats_post_count_update('decrement', $node->uid);
340
    }
341
  }
342
}
343

    
344
/**
345
 * Implements hook_comment_insert().
346
 */
347
function user_stats_comment_insert($comment) {
348
  if (variable_get('user_stats_count_comments', TRUE)) {
349
    if ($comment->uid == NULL) {
350
      $comment->uid = 0;
351
    }
352
    $post_count_content_types = variable_get('user_stats_included_content_types', array());
353
    $node = node_load($comment->nid);
354

    
355
    if (empty($post_count_content_types) || in_array($node->type, $post_count_content_types)) {
356
      if ($comment->status == COMMENT_PUBLISHED) {
357
        user_stats_post_count_update('increment', $comment->uid);
358
      }
359
    }
360
  }
361
  global $user;
362
  // User IP addresses are only interesting if they are posting the content.
363
  if (TRUE && $comment->uid == $user->uid) {
364
    // User IP addresses are only interesting if they are posting the content.
365
    user_stats_ip_address_update($user->uid, ip_address());
366
  }
367
}
368

    
369
/**
370
 * Implements hook_comment_update().
371
 */
372
function user_stats_comment_update($comment) {
373
  if (variable_get('user_stats_count_comments', TRUE)) {
374
    if ($comment->uid == NULL) {
375
      $comment->uid = 0;
376
    }
377
    $post_count_content_types = variable_get('user_stats_included_content_types', array());
378
    $node = node_load($comment->nid);
379

    
380
    if (empty($post_count_content_types) || in_array($node->type, $post_count_content_types)) {
381
      if ($comment->status == COMMENT_PUBLISHED) {
382
        user_stats_post_count_update('reset', $comment->uid);
383
      }
384
    }
385
  }
386
}
387

    
388
/**
389
 * Implements hook_comment_delete().
390
 */
391
function user_stats_comment_delete($comment) {
392
  if (variable_get('user_stats_count_comments', TRUE)) {
393
    if ($comment->uid == NULL) {
394
      $comment->uid = 0;
395
    }
396
    $post_count_content_types = variable_get('user_stats_included_content_types', array());
397
    $node = node_load($comment->nid);
398

    
399
    if (empty($post_count_content_types) || in_array($node->type, $post_count_content_types)) {
400
      if ($comment->status == COMMENT_PUBLISHED) {
401
        user_stats_post_count_update('decrement', $comment->uid);
402
      }
403
    }
404
  }
405
}
406

    
407
/**
408
 * Implements hook_cron().
409
 *
410
 * We slowly work through all users without a post count
411
 * updating them.
412
 */
413
function user_stats_cron() {
414
  if (variable_get('user_stats_rebuild_stats', TRUE) &&
415
    (variable_get('user_stats_count_posts', TRUE) ||
416
    variable_get('user_stats_count_comments', TRUE))) {
417

    
418
    $sql = "SELECT uid FROM {users} WHERE uid NOT IN
419
      (SELECT uid FROM {user_stats_values} WHERE name = 'post_count')";
420
    // Update 25 users per cron run.
421
    $result = db_query_range($sql, 0, variable_get('user_stats_user_per_cron', '25'));
422
    $users_updated = FALSE;
423
    foreach ($result as $update_user) {
424
      user_stats_post_count_update('reset', $update_user->uid);
425
      $users_updated = TRUE;
426
    }
427
    // If all users have been updated we'll avoid running this expensive
428
    // query again by setting the following flag.
429
    if (!$users_updated) {
430
      variable_set('user_stats_rebuild_stats', FALSE);
431
    }
432
  }
433
  // Fire rules day_older event.
434
  // This may seem grossly inefficient, but testing showed that, even firing
435
  // the event for ~100 users, takes less than a second to run when there are
436
  // no rules using this event. With a rule (that adds a role if the user has
437
  // been a member for over 1,000 days) cron took an extra ~40 seconds to run.
438
  // Basically, this has no potential to harm a site's performance, unless a
439
  // rule is configured.
440
  // Having said this: if there's a better way, please raise a bug report!
441
  if (module_exists('rules')) {
442
    $sql  = "SELECT uid FROM {users} u ";
443
    // ((last cron - created) - (time() - created)) > one day
444
    $sql .= "WHERE (FLOOR((:request_time-created)/(60*60*24))-FLOOR((:cron_last-created)/(60*60*24)))>0 AND uid>0";
445
    $result = db_query($sql, array(':request_time' => REQUEST_TIME, ':cron_last' => variable_get('cron_last', REQUEST_TIME)));
446
    $reset_user_count = 0;
447
    foreach ($result as $update_user) {
448
      rules_invoke_event('user_stats_day_older', $update_user->uid);
449
    }
450
  }
451

    
452
  if (variable_get('user_stats_track_ips', TRUE)) {
453
    // Delete items from the IP log that are past expiry.
454
    db_delete('user_stats_ips')
455
      ->condition('first_seen_timestamp', REQUEST_TIME - variable_get('user_stats_flush_ips_timer', 31536000), '<')
456
      ->execute();
457
  }
458
}
459

    
460
/**
461
 * Implements hook_user_login().
462
 */
463
function user_stats_user_login(&$edit, $account) {
464
  if (variable_get('user_stats_count_logins', TRUE)) {
465
    user_stats_login_count_update('increment', $account->uid);
466
  }
467
  user_stats_ip_address_update($account->uid, ip_address());
468
}
469

    
470
/**
471
 * Implements hook_user_logout().
472
 */
473
function user_stats_user_logout($account) {
474
  user_stats_ip_address_update($account->uid, ip_address());
475
}
476

    
477
/**
478
 * Helper function to get the last post created by the user.
479
 *
480
 * @param $account
481
 *   User object.
482
 *
483
 * @return
484
 *   Unix timestamp: date of the last post (node or comment).
485
 */
486
function _user_stats_last_post($uid) {
487
  $sql  = "SELECT MAX(created) FROM {node} WHERE status=:status AND uid=:uid";
488
  $all_content_types = node_type_get_types();
489
  $post_count_content_types = variable_get('user_stats_included_content_types', array());
490
  $where = "";
491
  // If some, but not all, content types have been selected in the admin
492
  // interface add a WHERE clause to select only them.
493
  if (!empty($post_count_content_types) && array_keys($all_content_types) != array_keys($post_count_content_types)) {
494
    $content_types = "'" . implode("','", $post_count_content_types) . "'";
495
    $where = ' AND type IN (' . $content_types . ')';
496
  }
497
  $sql .= $where;
498
  // TODO Please convert this statement to the D7 database API syntax.
499
  $max_node = db_query($sql, array(':status' => COMMENT_PUBLISHED, ':uid' => $uid))->fetchField();
500

    
501
  $sql  = "SELECT MAX(c.changed) FROM {comment} c ";
502
  $where = " WHERE c.status=:status AND c.uid=:uid ";
503
  $join = "";
504
  if (!empty($post_count_content_types) && array_keys($all_content_types) != array_keys($post_count_content_types)) {
505
    $join = " INNER JOIN {node} n ON c.nid=n.nid ";
506
    $where .= 'AND n.type IN (' . $content_types . ')';
507
  }
508
  $sql .= $join . $where;
509
  // TODO Please convert this statement to the D7 database API syntax.
510
  $max_comments = db_query($sql, array(':status' => COMMENT_PUBLISHED, ':uid' => $uid))->fetchField();
511

    
512
  if (is_null($max_node) && is_null($max_comments)) {
513
    return FALSE;
514
  }
515
  else if ($max_node > $max_comments) {
516
    return $max_node;
517
  }
518
  else if ($max_node <= $max_comments) {
519
    return $max_comments;
520
  }
521
}
522

    
523
/**
524
 * Implements hook_views_api().
525
 *
526
 * Other Views hooks in user_stats.views.inc.
527
 */
528
function user_stats_views_api() {
529
  return array(
530
    'api' => '2.0',
531
    'path' => drupal_get_path('module', 'user_stats') . '/views',
532
  );
533
}
534

    
535
/**
536
 * Actions/Rules hooks and implementing functions.
537
 *
538
 * (we don't do Triggers as the API doesn't seem complete -- having to use
539
 * _trigger_get_hook_aids() for example). Patches welcome for this, as long
540
 * as they do not use private member functions!
541
 *
542
 * Most Rules hooks are in user_stats.rules.inc.
543
 */
544

    
545
/**
546
 * Implements hook_action_info().
547
 */
548
function user_stats_action_info() {
549
  return array(
550
    'user_stats_post_count_reset_action' => array(
551
      'label' => t('Reset user post count'),
552
      'type' => 'user',
553
      'configurable' => FALSE,
554
      'triggers' => array(
555
        'nodeapi_delete',
556
        'nodeapi_insert',
557
        'nodeapi_update',
558
        'nodeapi_view',
559
        'comment_view',
560
        'comment_insert',
561
        'comment_update',
562
        'comment_delete',
563
        'user_login',
564
        'user_logout',
565
      ),
566
    ),
567
    'user_stats_login_count_reset_action' => array(
568
      'label' => t('Reset user login count'),
569
      'type' => 'user',
570
      'configurable' => FALSE,
571
      'triggers' => array(
572
        'nodeapi_delete',
573
        'nodeapi_insert',
574
        'nodeapi_update',
575
        'nodeapi_view',
576
        'comment_view',
577
        'comment_insert',
578
        'comment_update',
579
        'comment_delete',
580
        'user_login',
581
        'user_logout',
582
      ),
583
    ),
584
  );
585
}
586

    
587
/**
588
 * Implementation of a Drupal action.
589
 *
590
 * Resets a user's post count.
591
 */
592
function user_stats_post_count_reset_action(&$object, $context = array()) {
593
  if (isset($object->uid)) {
594
    $uid = $object->uid;
595
  }
596
  elseif (isset($context['uid'])) {
597
    $uid = $context['uid'];
598
  }
599
  else {
600
    global $user;
601
    $uid = $user->uid;
602
  }
603
  user_stats_post_count_update('reset', $uid);
604
}
605

    
606
/**
607
 * Implementation of a Drupal action.
608
 * Resets a user's login count.
609
 */
610
function user_stats_login_count_reset_action(&$object, $context = array()) {
611
  if (isset($object->uid)) {
612
    $uid = $object->uid;
613
  }
614
  elseif (isset($context['uid'])) {
615
    $uid = $context['uid'];
616
  }
617
  else {
618
    global $user;
619
    $uid = $user->uid;
620
  }
621
  user_stats_login_count_update('reset', $uid);
622
}
623

    
624
/**
625
 * Implements hook_user_stats().
626
 *
627
 * Invoke the Rules module.
628
 */
629
function user_stats_user_stats($type, $op, $uid, $value) {
630
 /*
631
  if (module_exists('rules')) {
632
    rules_invoke_event('user_stats_' . $type . '_' . $op, $uid, $value);
633
  }
634
*/
635
}
636

    
637
/**
638
 * Token hook implementations
639
 */
640

    
641
/**
642
 * Implements hook_token_values().
643
 */
644
function user_stats_token_values($type, $object = NULL) {
645
  switch ($type) {
646
    case 'user':
647
    case 'all':
648
      if (isset($object)) {
649
        // Think this is sometimes an array (please raise this as an issue if wrong).
650
        $object = (object) $object;
651
        $uid = $object->uid;
652
      }
653
      else {
654
        global $user;
655
        $uid = $user->uid;
656
      }
657

    
658
      // Check_plain added as per Greggles suggestion: http://drupal.org/node/166305#comment-665874
659
      $values['reg-days']               = check_plain(user_stats_get_stats('reg_days', $uid));
660
      $values['login-days']             = check_plain(user_stats_get_stats('login_days', $uid));
661
      $values['post-days']              = check_plain(user_stats_get_stats('post_days', $uid));
662
      $values['post-count']             = check_plain(user_stats_get_stats('post_count', $uid));
663
      $values['ip-address']             = check_plain(user_stats_get_stats('ip_address', $uid));
664
      $values['login-count']            = check_plain(user_stats_get_stats('login_count', $uid));
665
      return $values;
666
  }
667
}
668

    
669
/**
670
 * Implements hook_token_list().
671
 */
672
function user_stats_token_list($type = 'all') {
673
  if ($type == 'user' || $type == 'all') {
674
    $tokens['user']['reg-days']               = t('Number of days since the user registered');
675
    $tokens['user']['login-days']             = t('Number of days since the user logged in');
676
    $tokens['user']['post-days']              = t('Number of days since the user posted');
677
    $tokens['user']['post-count']             = t("User's post count");
678
    $tokens['user']['ip-address']             = t("User's IP address");
679
    $tokens['user']['login-count']            = t("User's login count");
680
    return $tokens;
681
  }
682
}
683

    
684
/**
685
 * Checks whether a statistic is set for a given user.
686
 *
687
 * @param $uid
688
 *   User ID of the user who's statistics should be checked.
689
 * @param $statistic
690
 *   What statistic to check.
691
 */
692
function user_stats_isset($statistic, $uid) {
693
  $result = db_query("SELECT COUNT(*)
694
    FROM {user_stats_values}
695
    WHERE uid = :uid AND name = :name",
696
    array(':uid' => $uid, ':name' => $statistic))->fetchField();
697

    
698
  if ($result > 0) {
699
    return TRUE;
700
  }
701

    
702
  return FALSE;
703
}
704

    
705
/**
706
 * Manage the login count of a given user.
707
 *
708
 * @param $uid
709
 *   Unique id of the user who's record should be updated.
710
 * @param $op
711
 *   Whether the user login count should be incremented, decremented, or reset.
712
 *   Possible values are:
713
 *    - 'increment'
714
 *    - 'decrement'
715
 *    - 'reset'
716
 */
717
function user_stats_login_count_update($op, $uid) {
718
  if (!is_numeric($uid)) {
719
    return;
720
  }
721

    
722
  switch ($op) {
723
    case 'increment':
724
      if (user_stats_isset('login_count', $uid)) {
725
        // Update existing value.
726
        db_update('user_stats_values')
727
          ->expression('value', 'value + :value', array(':value' => 1))
728
          ->condition('name', 'login_count')
729
          ->condition('uid', $uid)
730
          ->execute();
731
      }
732
      else {
733
        // If there isn't a value insert it.
734
        $id = db_insert('user_stats_values')
735
          ->fields(array(
736
            'name' => 'login_count',
737
            'uid' => $uid,
738
            'value' => 1,
739
          ))
740
          ->execute();
741
      }
742
      break;
743
    case 'decrement':
744
      if (user_stats_isset('login_count', $uid)) {
745
        // Update existing value.
746
        $count = (user_stats_cache_get('login_count', $uid) - 1);
747
        db_update('user_stats_values')
748
          ->expression('value', 'value + :value', array(':value' => 1))
749
          ->condition('name', 'login_count')
750
          ->condition('uid', $uid)
751
          ->execute();
752
      }
753
      else {
754
        // If there isn't a value insert it.
755
        $id = db_insert('user_stats_values')
756
          ->fields(array(
757
            'name' => 'login_count',
758
            'uid' => $uid,
759
            'value' => 0,
760
          ))
761
          ->execute();
762
      }
763
      break;
764
    case 'reset':
765
      db_delete('user_stats_values')
766
        ->condition('name', 'login_count')
767
        ->condition('uid', $uid)
768
        ->execute();
769
      break;
770
  }
771
  // Flush token cache.
772
  //if (module_exists('token')) {
773
    //token_get_values('user', NULL, TRUE);
774
  //}
775
  // Flush internal cache.
776
  user_stats_cache_set('reset', $uid);
777
  // Allow modules to react to a statistic change.
778
  module_invoke_all('user_stats', 'login_count', $op, $uid, user_stats_get_stats('login_count', $uid));
779
}
780

    
781
/**
782
 * Manage the post count of a given user.
783
 *
784
 * @param $uid
785
 *   Unique id of the user who's record should be updated.
786
 * @param $op
787
 *   Whether the user post count should be incremented, decremented, or reset.
788
 *   The default is to increment. Possible values are:
789
 *   'increment'
790
 *   'decrement'
791
 *   'reset'
792
 */
793
function user_stats_post_count_update($op, $uid) {
794
  if (!is_numeric($uid)) {
795
    return;
796
  }
797

    
798
  switch ($op) {
799
    case 'increment':
800
      if (user_stats_isset('post_count', $uid)) {
801
        //@TODO: Previous query tried to update and add in one query,
802
        // that wasn't working.
803
        $count = (user_stats_get_stats('post_count', $uid) + 1);
804

    
805
        db_update('user_stats_values')
806
          ->fields(array(
807
            'value' => $count,
808
          ))
809
          ->condition('name', 'post_count')
810
          ->condition('uid', $uid)
811
          ->execute();
812

    
813
        // Flush internal cache.
814
        user_stats_cache_set('reset', $uid);
815
      }
816
      else {
817
        user_stats_post_count_update('reset', $uid);
818
      }
819
      break;
820
    case 'decrement':
821
      if (user_stats_isset('post_count', $uid)) {
822
        //@TODO: Same issue as 'increment'.  Previous query tried to update
823
        // and add in one query... that wasn't working
824
        $count = (user_stats_get_stats('post_count', $uid) - 1);
825

    
826
        db_update('user_stats_values')
827
          ->fields(array(
828
            'value' => $count,
829
          ))
830
          ->condition('name', 'post_count')
831
          ->condition('uid', $uid)
832
          ->execute();
833
        // Flush internal cache.
834
        user_stats_cache_set('reset', $uid);
835
      }
836
      else {
837
        user_stats_post_count_update('reset', $uid);
838
      }
839
      break;
840
    case 'reset':
841
      $total_count = 0;
842
      if (variable_get('user_stats_count_posts', TRUE)) {
843
        $sql  = "SELECT COUNT(*) FROM {node} WHERE uid = :uid AND status = 1";
844
        $post_count_content_types = variable_get('user_stats_included_content_types', array());
845
        if (!empty($post_count_content_types)) {
846
          $content_types = "'" . implode("','", $post_count_content_types) . "'";
847
          $where = ' AND type IN (' . $content_types . ')';
848
          $sql .= $where;
849
        }
850
        $node_count = db_query($sql, array(':uid' => $uid))->fetchField();
851
        $total_count += $node_count;
852
      }
853
      if (variable_get('user_stats_count_comments', TRUE)) {
854
        // COMMENT_PUBLISHED is now 1 in D7, and COMMENT_UNPUBLISHED is 0 
855
        $sql = "SELECT COUNT(*) FROM {comment} c 
856
          INNER JOIN {node} n ON c.nid = n.nid
857
          WHERE c.uid = :uid AND c.status = 1 AND n.status = 1";
858
        if (!empty($post_count_content_types)) {
859
          $where = ' AND n.type IN (' . $content_types . ')';
860
          $sql .= $where;
861
        }
862

    
863
        $comments_count = db_query($sql,  array(':uid' => $uid))->fetchField();
864
        $total_count += $comments_count;
865
      }
866
      db_delete('user_stats_values')
867
        ->condition('name', 'post_count')
868
        ->condition('uid', $uid)
869
        ->execute();
870
      $id = db_insert('user_stats_values')
871
        ->fields(array(
872
        'name' => 'post_count',
873
        'uid' => $uid,
874
        'value' => $total_count,
875
      ))
876
      ->execute();
877
      // Prime the cache, this will be used by module_invoke_all() below.
878
      user_stats_cache_set('post_count', $uid, $total_count);
879
      break;
880
  }
881
  // Flush token cache
882
  //if (module_exists('token')) {
883
    //token_get_values('user', NULL, TRUE);
884
  //}
885

    
886
  // Allow modules to react to a statistic change.
887
  module_invoke_all('user_stats', 'post_count', $op, $uid, user_stats_get_stats('post_count', $uid));
888
}
889

    
890
/**
891
 * Update the IP address of a given user.
892
 *
893
 * The IP address is not updated if it is the same as the last recorded IP,
894
 * however, if the user has IP address A, then switches to IP address B
895
 * and back to A again, A will be recorded twice. This is to keep an accurate
896
 * log of IP addresses used by users.
897
 *
898
 * @param $uid
899
 *   User ID of user who's IP is being updated.
900
 * @param $ip_address
901
 *   IP address to assign to user.
902
 */
903
function user_stats_ip_address_update($uid, $ip_address) {
904
  if (!is_numeric($uid)) {
905
    return;
906
  }
907

    
908
  // Don't bother recording IPs of anonymous users, and don't record any
909
  // addresses if the config form tells us not to.
910
  if ($uid == 0 || !variable_get('user_stats_track_ips', TRUE)) {
911
    return;
912
  }
913
  $query = db_query_range("SELECT ip_address
914
    FROM {user_stats_ips}
915
    WHERE uid = :uid
916
    ORDER BY first_seen_timestamp DESC",
917
    0, 1, array(':uid' => $uid));
918

    
919
  if ($ip_address != $query->fetchField()) {
920
    // Reset internal cache.
921
    user_stats_cache_set('reset', $uid);
922
    $id = db_insert('user_stats_ips')
923
      ->fields(array(
924
        'uid' => $uid,
925
        'ip_address' => $ip_address,
926
        'first_seen_timestamp' => REQUEST_TIME,
927
      ))
928
      ->execute();
929
    // Allow modules to react to an IP address change.
930
    module_invoke_all('user_stats', 'ip_address', 'insert', $uid, $ip_address);
931
  }
932
}
933

    
934
/**
935
 * Resets statistics. Full stop.
936
 *
937
 * @param $statistic
938
 *   The name of the statistic to be reset.
939
 *   Corresponds with {user_stats_values}.name.
940
 */
941
function user_stats_reset_counts($statistic) {
942
  db_delete('user_stats_values')
943
    ->condition('name', $statistic)
944
    ->execute();
945
}
946

    
947
/**
948
 * Implements hook_user_insert to record ip of new user on registration.
949
 */
950
function user_stats_user_insert(&$edit, $account, $category) {
951
  global $user;
952
  if ($user->uid == 0) {
953
    $uid = $account->uid;
954
    $ip = ip_address();
955
    user_stats_ip_address_update($uid, $ip);
956
  }
957
}