<?php
/*
Plugin Name: 5sec Who's Online
Plugin URI: http://5sec-whos-online.webfactoryltd.com/
Description: Show how many users are online on your WP site. Widget, shortcode, dashboard widget, users module enhancement and theme functions included.
Author: Web factory Ltd
Version: 1.1
Author URI: http://www.webfactoryltd.com/
*/

// how many seconds after a visit is user considered to be online
define('WHOS_ONLINE_DELTA_ONLINE', 300);

// how often (in %) do we update the user's online; 100 means 100% - each request
define('WHOS_ONLINE_UPDATE_FREQ', 100);

// shortcode name, change if needed
define('WHOS_ONLINE_SHORTCODE', 'whos-online');


// **********************************
// !! DO NOT TOUCH BELOW THIS LINE !!
// **********************************

class wf_whos_online {
  private static $count = null;
  
  public function init() {
    // add shortcode
    global $shortcode_tags;
    if (isset($shortcode_tags[WHOS_ONLINE_SHORTCODE])) {
      add_action('admin_footer', array('wf_whos_online', 'warning'));
    } else {
      add_shortcode(WHOS_ONLINE_SHORTCODE, array('wf_whos_online', 'shortcode'));
    }

    // add shortcode support in sidebar text widget
    if (has_filter('widget_text', 'do_shortcode') === false) {
      add_filter('widget_text', 'do_shortcode');
    }

    add_filter('manage_users_columns', array('wf_whos_online', 'users_columns'), 10, 1);
    add_action('manage_users_custom_column', array('wf_whos_online', 'users_columns_last_visit'), 10, 3);
    add_action('wp_dashboard_setup', array('wf_whos_online', 'add_dashboard_widget'));
    
    self::update();

    return;
  } // init
  
  function dashboard_widget() {
    $out  = '<ul>';
    $out .= '<li>Total users online: {total_cnt}</li>';
    $out .= '<li>Guests online: {guests_cnt}</li>';
    $out .= '<li>Logged in users online: {loggedin_cnt}; {loggedin_list_links}</li>';
    $out .= '</ul>';
    
    $out = self::parse_text($out);
    echo $out;
  } // dashboard_widget
  
  function add_dashboard_widget() {
    wp_add_dashboard_widget('whos_online_dashboard_widget', 'Who\'s Online', array('wf_whos_online', 'dashboard_widget'));
  } // add_dashboard_widget
  
  function update() {
    // skip update based on update frequency
    if (WHOS_ONLINE_UPDATE_FREQ < rand(0, 100)) {
      return;
    }
    
    
    global $current_user;
    
    get_currentuserinfo();
    if ($current_user->ID) {
      update_user_meta($current_user->ID, 'whos_online_last_visit', current_time('timestamp'));
    }
    
    $users_online = get_transient('whos_online_users_online');
      
    $user_id = md5($_SERVER['SERVER_ADDR'] . @$_SERVER['HTTP_USER_AGENT']);
    $users_online[$user_id] = current_time('timestamp');
  
    foreach ($users_online as $user => $timestamp) {
      if ($timestamp + WHOS_ONLINE_DELTA_ONLINE > current_time('timestamp')) {
        $users_online2[$user] = $timestamp;
      } 
    }
    set_transient('whos_online_users_online', $users_online2);

    return;
  } // update
    
  function users_columns($cols) {
    $cols['last-visit'] = __('Last visit', 'Gameleon'); 
    
    return $cols;
  } // users_column
  
  function users_columns_last_visit($value, $column_name, $user_id) {
    $out = null;
    
    if ($column_name == 'last-visit') {
      $time = get_user_meta($user_id, 'whos_online_last_visit', 1);
      if (!$time) {
        $out = __('Unknown.', 'Gameleon');
      } else {
        if ((current_time('timestamp') - $time) < WHOS_ONLINE_DELTA_ONLINE) {
          $out = __('A few minutes ago.', 'Gameleon');
        } else {
          $out = date(get_option('date_format') . ' <\b\r /> ' . get_option('time_format'), $time);
        }
      }
    }
    
    return $out;
  } // users_columns_last_visit
  
  public function shortcode($atts, $content = null) {
    global $post;
    $out = '';

    // parse attributes and set some default values
    $atts = shortcode_atts(array('content'  => '',
                                  'debug' => false,
                                  'post_id' => $post->ID),
                           $atts);

    // check if text came from var or content
    if (!is_null($content)) {
      $atts['content'] = $content;
    }
    $atts['_org_content'] = $atts['content'];
    
    // if we have a post ID, replace $value with custom field values
    $atts['post_id'] = (int) $atts['post_id'];
    if ($atts['post_id']) {
      foreach ($atts as $param => $val) {
        if (substr($val, 0, 1) == '$' && substr($val, 0, 2) != '$$' && substr($param, 0, 1) != '_') {
          $atts[$param] = get_post_meta($post->ID, str_replace('$', '', $val), true);
        } elseif (substr($val, 0, 2) == '$$') {
          $atts[$param] = substr($val, 1);
        }
      } // foreach
    } // if post_id
    
    // debug?
    $atts['debug'] = (bool) $atts['debug'];
    if ($atts['debug']) {
      $out .= '<pre>';
      $out .= var_export($atts, true);
      //$out .= var_export(get_transient('whos_online_users_online'), 1);
      $out .= '</pre>';
    }
    
    $out .= self::parse_text($atts['content']);
    
    return $out;
  } // shortcode
  
  function parse_text($text) {
    $vars = self::get_count();
    $text = str_replace(array('{total_cnt}',      '{guests_cnt}',      '{loggedin_cnt}', 
                               '{loggedin_list}',  '{loggedin_li}',     '{loggedin_list_links}', '{loggedin_li_links}', 
                               '{loggedin_avatars}'),
                        array($vars['total_cnt'], $vars['guests_cnt'], $vars['loggedin_cnt'],
                               $vars['loggedin_list'], $vars['loggedin_li'], $vars['loggedin_list_links'], $vars['loggedin_li_links'],
                               $vars['loggedin_avatars']),
                        $text);
    
    return $text;
  
  } // parse text
  
  function get_count() {
    if (self::$count !== null) {
      return self::$count;
    }
    
    global $wpdb;
    $out = array();
    
    $users = get_transient('whos_online_users_online');
    $total_users_cnt = sizeof($users);
  
    $tmp = current_time('timestamp') - WHOS_ONLINE_DELTA_ONLINE;
    $logged_in_users = $wpdb->get_results("SELECT u.ID, u.user_login, u.user_nicename, u.user_email, u.display_name, um.meta_value as last_visit_ts
                                           FROM {$wpdb->usermeta} um
                                           LEFT JOIN {$wpdb->users} u ON u.ID = um.user_id
                                           WHERE meta_key = 'whos_online_last_visit' AND CAST(meta_value AS SIGNED) > {$tmp} ");
    $logged_in_users_cnt = sizeof($logged_in_users);
    $guests_cnt = $total_users_cnt - $logged_in_users_cnt;
    
    $logged_in_avatars = '';
    $logged_in_users_li = '';
    $logged_in_users_list = '';
    $logged_in_users_li_link = '';
    $logged_in_users_list_link = '';
    foreach ($logged_in_users as $user) {
      $user->posts_url = get_author_posts_url($user->ID, $user->user_nicename);
      $logged_in_users_list .= $user->display_name . ', ';
      $logged_in_users_li .= '<li>' . $user->display_name . '</li>';
      $logged_in_users_list_link .= '<a href="' . get_author_posts_url($user->ID, $user->user_nicename) . '">' .  $user->display_name . '</a>, ';
      $logged_in_users_li_link .= '<li><a href="' . get_author_posts_url($user->ID, $user->user_nicename) . '">' .  $user->display_name . '</a></li>';
      $logged_in_avatars .=  '<a href="' . get_author_posts_url($user->ID, $user->user_nicename) . '">'.  get_avatar($user->user_email, null, null, $user->user_nicename)  . '</a> ';

    }
    $logged_in_users_list = trim($logged_in_users_list, ', ');
    $logged_in_users_list_link = trim($logged_in_users_list_link, ', ');
    
    $logged_in_avatars = trim($logged_in_avatars, ', ');

    $out['total_cnt'] = '<span class="count">' . $total_users_cnt . '</span>';
    $out['guests_cnt'] = '<span class="count">' . $guests_cnt . '</span>';
    $out['loggedin_cnt'] = '<span class="count">' . $logged_in_users_cnt . '</span>';
    $out['loggedin_li'] = '<span class="count">' . $logged_in_users_li . '</span>';
    $out['loggedin_list'] = '<span class="usr">' . $logged_in_users_list . '</span>';
    $out['loggedin_li_links'] = '<span class="count">' . $logged_in_users_li_link . '</span>';
    $out['loggedin_list_links'] = '<span class="count">' . $logged_in_users_list_link . '</span>';
    $out['loggedin_users'] = $logged_in_users;
    $out['loggedin_avatars'] = $logged_in_avatars;
    
    self::$count = $out;
    return $out;
  } // get_count
  
  public function warning() {
    echo '<div id="message" class="error"><p><strong>Who\'s Online is not active!</strong> The shortcode [whos_online] is already in use by another plugin. Please refer to <a href="http://5sec-whos-online.webfactoryltd.com/">documentation</a> to solve this issue.</p></div>';
  
    return;
  } // warning
} // class wf_whos_online

class whos_online_widget extends WP_Widget {
 function whos_online_widget() {
   $widget_ops = array('classname' => 'whos_online', 'description' => __('Display how many users are online on your site and who they are. This is a widget for sidebar.', 'Gameleon') );
   $control_ops = array('width' => 400, 'height' => 400, 'id_base' => 'whos-online' );
   $this->WP_Widget('whos-online', __('GAMELEON: Who\'s Online', 'Gameleon'), $widget_ops, $control_ops );
 } // whos online widget

 function widget($args, $instance) {
   extract($args);

   $title = apply_filters('widget_title', $instance['title'] );

   echo $before_widget;
   if (!empty($title)) {
     echo $before_title . $title . $after_title;
   }
   echo '<div class="grid-sidebar"><div class="online-users">';
   if ($instance['filter']) {
     echo wpautop(wf_whos_online::parse_text($instance['content']));
   } else {
     echo wf_whos_online::parse_text($instance['content']);
   }
   echo '</div></div>';
   echo $after_widget;
 } // widget

  function update( $new_instance, $old_instance ) {
    $instance = $old_instance;

    $instance['title'] = strip_tags($new_instance['title']);
    $instance['content'] = $new_instance['content'];
    $instance['filter'] = isset($new_instance['filter']);

    return $instance;
 } // update

 function form( $instance ) {
   $defaults = array('title' => __('Who\'s Online?', 'Gameleon'), 'content' => "Total users online: {total_cnt}\nGuests online: {guests_cnt}\nRegistered online: {loggedin_cnt}; {loggedin_list} ", 'filter' => true);
   $instance = wp_parse_args((array) $instance, $defaults); ?>

  <p>
   <label for="<?php echo $this->get_field_id('title'); ?>"><?php echo __('Title:', 'Gameleon'); ?></label>
   <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" value="<?php echo $instance['title']; ?>" style="width:100%;" />
  </p>

  <p>
   <textarea class="widefat" cols="20" rows="12" id="<?php echo $this->get_field_id('content'); ?>" name="<?php echo $this->get_field_name('content'); ?>"><?php echo $instance['content']; ?></textarea>
  </p>
  
  <p><input id="<?php echo $this->get_field_id('filter'); ?>" name="<?php echo $this->get_field_name('filter'); ?>" type="checkbox" <?php checked(isset($instance['filter']) ? $instance['filter'] : 0); ?> />&nbsp;<label for="<?php echo $this->get_field_id('filter'); ?>"><?php _e('Automatically add paragraphs', 'Gameleon'); ?></label></p>
  
 <p>Available variables:</p>
  <ul style="list-style-type: circle; list-style-position: inside;">
       <li>{total_cnt} - <?php echo __('total number of users online', 'Gameleon'); ?></li>
    <li>{guests_cnt} - <?php echo __('number of guests/anonymous users online', 'Gameleon'); ?></li>
    <li>{loggedin_cnt} - <?php echo __('number of logged in users online', 'Gameleon'); ?></li> 
    <li>{loggedin_list} - <?php echo __('comma separated list of logged in users online', 'Gameleon'); ?></li>
    <li>{loggedin_list_links} - <?php echo __('comma separated list of logged in users online with links to their posts page', 'Gameleon'); ?></li>
    <li>{loggedin_avatars} - &lt;IMG&gt; <?php echo __('elements with logged in users (gr)avatars; img size 96x96', 'Gameleon'); ?></li>
  </ul>
 <?php
 }
} // whos online widget

// functions for template support
function get_whos_online() {
  return wf_whos_online::get_count();
} // get_whos_online

// hook our thing
add_action('init', array('wf_whos_online', 'init'));
add_action('widgets_init', 'wf_register_widgets');
function wf_register_widgets() {
  register_widget('whos_online_widget');
}
?>