<?php namespace ProcessWire;

/**
 * ProcessWire Session Viewer
 *
 * This module accompanies installation of the SessionHandlerDB module
 *
 * ProcessWire 3.x, Copyright 2021 by Ryan Cramer
 * https://processwire.com
 *
 */

class ProcessSessionDB extends Process {

	public static function getModuleInfo() {
		return array(
			'title' => __('Sessions', __FILE__), // getModuleInfo title          
			'summary' => __('Enables you to browse active database sessions.', __FILE__), // getModuleInfo summary 
			'version' => 5, 
			'permanent' => false, 
			'icon' => 'dashboard', 
			'requires' => array('SessionHandlerDB'),
			'page' => array(
				'name' => self::pageName,
				'parent' => 'access',
				'title' => 'Sessions',
			));
	}

	const pageName = 'sessions-db';

	/**
	 * Execute display of sessions
	 * 
	 * @return string
	 * 
	 */
	public function ___execute() {

		// clean out any stray sessions that may have not yet hit the gc probability
		// because we don't want them in the list that we display
		
		$modules = $this->wire()->modules;
		$config = $this->wire()->config;
		$session = $this->wire()->session;
		$database = $this->wire()->database;
		$input = $this->wire()->input;
		$pages = $this->wire()->pages;
		$users = $this->wire()->users;
		
		/** @var SessionHandlerDB $sessionHandlerDB */
		$sessionHandlerDB = $this->modules->get('SessionHandlerDB'); 
		$sessionHandlerDB->gc($config->sessionExpireSeconds); 
		
		$useIP = $sessionHandlerDB->useIP; 
		$useUA = $sessionHandlerDB->useUA; 

		$mins = (int) $input->post('mins');
		if(!$mins) $mins = (int) $session->get('ProcessSessionDB_mins'); 
		if(!$mins) $mins = 5; 
		$session->set('ProcessSessionDB_mins', $mins);

		/** @var InputfieldForm $form */
		$form = $modules->get('InputfieldForm');
	
		/** @var InputfieldInteger $field */
		$field = $modules->get('InputfieldInteger');
		$field->attr('name', 'mins'); 
		$field->attr('value', $mins);
		$field->label = sprintf($this->_n('Sessions active in last minute', 'Sessions active in last %d minutes', $mins), $mins); 
		$field->description = $this->_('Number of minutes'); 
		$field->collapsed = Inputfield::collapsedYes; 
		$form->add($field);

		$pagePaths = array();
		$userNames = array();
		$table = SessionHandlerDB::dbTableName;
		$limit = 500; 
		$seconds = $mins * 60;
		
		$sql = 	"SELECT user_id, pages_id, ip, ua, UNIX_TIMESTAMP(ts) " . 
				"FROM `$table` " . 
				"WHERE ts > DATE_SUB(NOW(), INTERVAL $seconds SECOND) " .
				"ORDER BY ts DESC LIMIT $limit";
	
		$query = $database->prepare($sql);	
		$query->execute();
		$numRows = $query->rowCount();

		if($numRows) {
			if($numRows == $limit) {
				// if more than 500, get an accurate count
				$numRows = $sessionHandlerDB->getNumSessions($mins * 60); 		
			}

			/** @var MarkupAdminDataTable $table */
			$table = $modules->get('MarkupAdminDataTable');
			$table->setSortable(false);
			$header = array(
				$this->_('Time'), 
				$this->_('User'), 
				$this->_('Page'),
			); 
			if($useIP) $header[] = $this->_('IP Addr');
			if($useUA) $header[] = $this->_('User Agent');
			$table->headerRow($header);

			// for first iteration
			$row = $query->fetch(\PDO::FETCH_NUM);
			
			while($row) {
				
				list($user_id, $pages_id, $ip, $ua, $ts) = $row; 
				
				$pages_id = (int) $pages_id;
				$user_id = (int) $user_id;

				if(isset($userNames[$user_id])) {
					$userName = $userNames[$user_id]; 

				} else {
					$user = $users->get($user_id);
					$userName = $user && $user->id ? $user->name : '.';
					if($userName !== '.') $userNames[$user_id] = $userName;
				}

				if(isset($pagePaths[$pages_id])) {
					$pagePath = $pagePaths[$pages_id]; 

				} else {
					$page = $pages->get($pages_id);
					$pagePath = $page->id ? $page->path : '.';
					if($pagePath !== '.') $pagePaths[$pages_id] = $pagePath;
				}

				$tr = array(wireRelativeTimeStr($ts), $userName, $pagePath);
				if($useIP) $tr[] = long2ip($ip);
				if($useUA) $tr[] = strip_tags($ua); // note MarkupAdminDataTable already entity encodes all output
				
				$table->row($tr);
			
				// for next iteration
				$row = $query->fetch(\PDO::FETCH_NUM);
			}
			
			$tableOut = $table->render();

		} else {
			$tableOut = "<p class='description'>" . $this->_('No active sessions') . "</p>";
		}
		
		$query->closeCursor();
		
		$out =
			"<h2>" .
			"<i id='SessionListIcon' class='fa fa-2x fa-fw fa-dashboard ui-priority-secondary'></i> " .
			sprintf($this->_n('%d active session', '%d active sessions', $numRows), $numRows) .
			"</h2>" .
			$tableOut; 
		
		if($config->ajax) return $out;

		/** @var InputfieldMarkup $markup */
		$markup = $modules->get('InputfieldMarkup');
		$markup->value = "<div id='SessionList'>$out</div>";
		$form->add($markup);

		/** @var InputfieldSubmit $submit */
		$submit = $modules->get('InputfieldSubmit');
		$submit->attr('value', $this->_('Refresh'));
		$submit->icon = 'refresh';
		$submit->attr('id+name', 'submit_session');
		$submit->showInHeader();
		$form->add($submit);
		
		return $form->render();
	}	
}
