Location: PHPKode > projects > Obsessive Website Statistics > ows/include/filter_output.inc.php
<?php
/*
	$Id: filter_output.inc.php 87 2007-08-14 06:00:25Z randomperson83 $

	Obsessive Web Statistics
    Copyright (C) 2007 Dustin Spicuzza <hide@address.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	
	Contains output functions for filters
	
	TODO: Add a graphing output function

*/

/**
 	ResultTable
 
 	Shows an associative array in a table format. This is probably way too complicated,
	but it works for the moment. Variables:
 
 	@param array 	$headers	Note: You can specify this option when you use SQLSelect to do a SELECT(). 
 								
 								Note: The inner array of this parameter can be created with col_description();
 
 								This is an array of arrays that denotes each column to be shown at the top 
 								of the table. If it is associative, then the columns will be matched
 								by key. Otherwise, it will be done in numerical order. If an entry is
 								not found in this array for the row, then a default value will be used.
 								The array will be formatted like this:
 
 									array('column_name' => array('Column Name Shown to User', $process_array), ...)
 								
 								or
 
 									array(array('Column Name Shown to User', $process_array), ...)
 								
 								column_name should be the actual SQL column, including things like SUM or distinct.
 								Basically, whatever db_fetch_assoc returns. See global $db_key_map for a better
 								example.
 
 								$process_array is an optional array. It contains a number of option keys, and 
 								if an option key is not set, then the processing is not done. The possible 
  								option keys currently are:
 	
 								ww -> length to wordwrap at, not set means dont wrap
 								href -> this is prefixed to the contents of the column, 
 								and then it is turned into a link
 								bold -> column is made bold
 								filesize -> if set, this interprets the column as a filesize
 
 **/

class ResultTable{

	public $headers = array();
	
	private $t_process = array();
	private $t_headers = array();
	
	function __construct ($headers = false){
		if ($headers !== false)
			$this->$headers = $headers;
	}
	
	/*
		showTableStart
		
		Shows a row of <table><tr><th> ... </th></tr>. Does not show the row contents
		that was passed to it.
		
	*/
	function showTableStart($row, $headers = false){
	
		global $db_key_map;
	
		// this loop is supposed to create the $this->t_process array,
		// among other things. 
	
		$i = -1;
		foreach ($row as $k => $v){
		
			$i += 1;
			$set = false;
			
			// try and use the user-provided mapping
			if ($headers !== false){
			
				// look for associative keys first
				if (array_key_exists($k,$headers) && isset($headers[$k][0])){
					
					// found the header, save it
					$this->t_headers[] = $headers[$k][0];
					
					// if a process array exists, set that
					if (isset($headers[$k][1]))
						$this->t_process[$k] = $headers[$k][1];
						
					$set = true;
				}
				
				// next, handle any numeric keys
				else if (array_key_exists($i,$headers) && isset($headers[$i][0])){
					
					// found the header, save it
					$this->t_headers[] = $headers[$i][0];
					
					// if a process array exists, set that
					if (isset($headers[$i][1]))
						$this->t_process[$k] = $headers[$i][1];
						
					$set = true;
				}
			}
			
			// fallback to default column names (which, could be set by a plugin through SQLSelect)
			if (!$set){
			
				if (isset($db_key_map[$k]) && isset($db_key_map[$k][0])){
					$this->t_headers[] = $db_key_map[$k][0];
					if (isset($db_key_map[$k][1]))
						$this->t_process[$k] = $db_key_map[$k][1];
				// no defaults set, use the SQL name
				}else
					$this->t_headers[] = $k;
			}
			
			// verify that a process array exists
			if (!array_key_exists($k,$this->t_process))
				$this->t_process[$k] = null;
		}
		
		echo "<table class=\"decorate\">\n\t<tr><th>" . implode("</th><th>",$this->t_headers) . "</th></tr>\n";
	
	}
	
	/*
		Outputs a table row from an associative array. 
	*/
	function showTableRow($row){
			
		echo "<tr>";
		array_walk($row,array($this,'process'),$this->t_process);
		echo "</tr>\n";	
	}


	// this walks the returned element set and processes them before output to the table
	private function process($item, $key, $process){

		
		$s_item = htmlentities($item);

		if (is_array($process[$key])){
			if (isset($process[$key]) && array_key_exists('filesize',$process[$key]))
				$s_item = get_file_size($s_item);
			if (isset($process[$key]) && array_key_exists('ww',$process[$key]) && ($ival = intval($process[$key]['ww'])) > 0)
				$s_item = wordwrap($s_item,$ival,'<br/>',true);
			if (isset($process[$key]) && array_key_exists('href',$process[$key]))
				$s_item = '<a rel="nofollow external" href="' . htmlentities(rawurldecode($process[$key]['href'] . $item)) . "\">$s_item</a>";
			if (isset($process[$key]) && array_key_exists('bold',$process[$key]))
				$s_item = "<strong>$s_item</strong>";
		}
		
		if (is_numeric($s_item))
			echo "<td style=\"text-align:center\">$s_item</td>";
		else
			echo "<td>$s_item</td>";	
	}

}


// wrapper around the ResultTable class to echo out the
// entire table set of an SQL operation. This wrapper provides functionality
// to 'page' through result sets as well. 

// TODO: It needs to be more modular, possibly make this apart of the ResultTable class
// Why isn't this an output plugin? 

/*
	show_result_table
	
	Wrapper around the ResultTable class to echo out the entire tableset of an SQL 
	operation. Of course, this wrapper provides functionality to 'page' through result sets
	as well. 
	
	$query 		SQLSelect object. If false, then result must not be false. If you provide a
				object here, then this routine will attempt to provide 'paging' through the
				result sets. 
				
	$result		Result returned from db_query.
*/


function show_result_table($query,$result = false, $headers = false){

	global $cfg;
	
	if ($query !== false){
	
		list($row_start,$max_rows) = get_query_limits($query);
		
		if (($q = $query->generateQuery()) === false)
			return false;

		$time = microtime(true);
			
		if (!db_is_valid_result($result = db_query($q)))
			return false;
			
			
		$totaltime = ((int)((microtime(true) - $time) * 1000))/1000;
			
	}else if (!db_is_valid_result($result)){

		show_error("Invalid parameters provided to show_result_table!");
		return false;
	}
	
	// this isn't an error... it just means we don't actually have to do anything
	if (!db_has_rows($result)){
		echo "<p>No results to show.</p>";
		return true;
	}
	
	$table = new ResultTable($headers);
	
	$row = db_fetch_assoc($result);
	$table->showTableStart($row, $headers);
	$table->showTableRow($row);
	
	// show all rows
	$rows = 1;
	while(($row = db_fetch_assoc($result)) !== false){
		$rows += 1;
		$table->showTableRow($row);
	}
	echo "</table>";
	
	// now, show paging stuff
	if ($query !== false){
		echo "<div class=\"tabletime\">Execution Time: $totaltime seconds</div>";
		show_query_paging($rows,$row_start,$max_rows);
	}
	return true;
}




/*
	get_query_limits
	
	This applies paging limits to a query object. 

		$query			The query object that you want to display
		$in_subquery	Set this to true if you're using this from a subquery.
		
	returns array ($row_start,$max_rows);

*/
global $_show_result_query_num;
$_show_result_query_num = -1;

function get_query_limits(&$query, $in_subquery = false){

	global $cfg, $_show_result_query_num;

	// initialization
	
	if (!$in_subquery){
		$_show_result_query_num += 1;
		$show_result_query_num = $_show_result_query_num;	// don't change the static param
	}else{
		$show_result_query_num = $_show_result_query_num + 1;
	}
	
	// ensure this param isn't too big.. override the limit if we cant figure it out
	if ($query->limit == -1 || !is_numeric($query->limit) || $query->limit > $cfg['db_row_limit'])
		$max_rows = $cfg['db_row_limit'];
	else
		$max_rows = $query->limit;
	
	
	$row_start = 0;
	$limit_high = $max_rows;
	
	// detect limit stuff
	if (($post = get_post_var("ows_select_$show_result_query_num")) != ""){

	
		// try to parse this thing
		$limits = explode(',',$post);
		if (count($limits) == 2){
			$row_start = intval($limits[0]);
			$limit_high = intval($limits[1]) - $row_start;
			
			// dont allow more rows than allowed
			if ($limit_high <= 0 || $limit_high > $max_rows)
				$limit_high = $max_rows;	
		}
	}
	
	
	// otherwise use built in limits
	
	$query->sql_calc_found_rows = true;
	$query->limit = "$row_start,$limit_high";

	return array($row_start,$max_rows);
	
}


/*
	Use this to show a paging mechanism for pages of output

		$rows 			number of rows returned for the output query
		$row_start		What row did the previous query start at?
*/
function show_query_paging($rows,$row_start,$max_rows){
	
	global $_show_result_query_num;

	// figure out the number of total rows
	if (!db_is_valid_result($result = db_query("SELECT FOUND_ROWS();")))
		return;
	
	$row = db_fetch_row($result);
	$found_rows = $row[0];

	// now, figure out how many pages we need to display, only if we need to
	if ($rows != $found_rows){
		
		echo "<p>$row_start - " . ($row_start + $rows) . " of $found_rows shown<br/>";

		
		$total_pages = intval(ceil($found_rows / $max_rows));
		$current_page = intval(floor($row_start / $max_rows));	// probably a good approximation
		$start = 0;
		
		if ($total_pages > 11){ // figure out where we currently are, then go five rows each way
			$start = ($current_page > 6 ? $current_page - 5 : 0);
		}
		
		// this cant possibly be good form.. 
		if ($current_page != 0)
			echo "<a onclick=\"return submit_next_page('" . htmlentities(get_get_var('filter')) . "','$_show_result_query_num','0,$max_rows');\" href=\"#\">&laquo;&laquo;</a> <a onclick=\"return submit_next_page('" . htmlentities(get_get_var('filter')) . "','$_show_result_query_num','" . (($current_page-1) * $max_rows) . ',' . (($current_page * $max_rows)-1) . "');\" href=\"#\">&laquo;</a>";
		
		
		$end = $start + 11 > $total_pages ? $total_pages : $start + 11;
		
		// i think this may be wrong..  what if the plugin decides to show more tables
		// the next time? There needs to be a good way to tag a table/query uniquely somehow so that
		// multiple table sets don't get horribly confused.
		for ($i = $start;$i < $end;$i++){
			if ($i != $current_page)
				echo " <a onclick=\"return submit_next_page('" . htmlentities(get_get_var('filter')) . "','$_show_result_query_num','" . ($i * $max_rows) . ',' . ((($i+1) * $max_rows)-1) . "');\" href=\"#\">" . ($i+1) . "</a> ";
				
			else
				echo ' ' . ($i+1) . ' ';
		}
		
		
		if ($current_page + 1 != $end)
			echo "<a onclick=\"return submit_next_page('" . htmlentities(get_get_var('filter')) . "','$_show_result_query_num','" . (($current_page+1) * $max_rows) . ',' . ((($current_page+2) * $max_rows)-1) . "');\" href=\"#\">&raquo;</a> <a onclick=\"return submit_next_page('" . htmlentities(get_get_var('filter')) . "','$_show_result_query_num','" . (($total_pages-1) * $max_rows) . "," . ($total_pages * $max_rows) . "');\" href=\"#\">&raquo;&raquo;</a>";
			
		
		echo "<input class=\"ows_select\" name=\"ows_select_$_show_result_query_num\" type=\"hidden\" value=\"$row_start,$max_rows\" /></p>";
	}
}




?>
Return current item: Obsessive Website Statistics