Location: PHPKode > projects > Banshee PHP Framework > public/slimstat/page/details.php
<?php

/*
 * SlimStat: simple web analytics
 * Copyright (C) 2010 Pieces & Bits Limited
 *
 * 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 2 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, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

// detect whether this is an ajax request

$ajax_capable = ( array_key_exists( 'slimstatajax', $_COOKIE ) && $_COOKIE['slimstatajax'] == 1 );
$ajax_request = ( array_key_exists( 'ajax', $_REQUEST ) && $_REQUEST['ajax'] == 1 );

// set up fields

$time_fields = array( 'yr', 'mo', 'dy', 'hr', 'mi' );
$hit_fields = array( 'resource', 'country', 'language', 'browser', 'version', 'platform', 'resolution' );
$visit_fields = array( 'remote_ip', 'search_terms', 'domain', 'referrer', 'start_resource', 'end_resource', 'hits' );

// set up filters

$filters = array();
$has_filters = false;
foreach ( $time_fields as $key ) {
	if ( array_key_exists( 'filter_'.$key, $_GET ) ) {
		$filters[$key] = $_GET['filter_'.$key];
	}
}
foreach ( array_merge( $hit_fields, $visit_fields ) as $key ) {
	if ( array_key_exists( 'filter_'.$key, $_GET ) ) {
		$filters[$key] = $_GET['filter_'.$key];
		$has_filters = true;
	}
}

if ( array_key_exists( 'QUERY_STRING', $_SERVER ) && $_SERVER['QUERY_STRING'] == 'today' ) {
	$filters['yr'] = date( 'Y' );
	$filters['mo'] = date( 'n' );
	$filters['dy'] = date( 'j' );
}

if ( array_key_exists( 'yr', $filters ) ) {
	$filters['yr'] = valid_yr( $filters['yr'] );
}
if ( array_key_exists( 'mo', $filters ) ) {
	$filters['mo'] = valid_mo( $filters['mo'] );
}

if ( !array_key_exists( 'yr', $filters ) ) {
	$filters['yr'] = date( 'Y' );
}
if ( !array_key_exists( 'mo', $filters ) ) {
	$filters['mo'] = date( 'n' );
}

if ( array_key_exists( 'dy', $filters ) ) { // do this after yr and mo are set
	$filters['dy'] = valid_dy( $filters['dy'], $filters['mo'], $filters['yr'] );
}

$is_filtering_visits_only = false;
foreach ( $visit_fields as $key ) {
	if ( array_key_exists( $key, $filters ) ) {
		$is_filtering_visits_only = true;
		break;
	}
}

// go

function render_page() {
	global $i18n, $curr_data, $prev_data, $ajax_request, $ajax_capable, $filters, $prev_filters;
	
	if ( array_key_exists( 'format', $_GET ) && $_GET['format'] == 'xml' ) {
		
		$curr_data = load_data( $filters );
		include( realpath( dirname( __FILE__ ) ).'/details_xml.php' );
		render_page_xml();
		
	} elseif ( array_key_exists( 'format', $_GET ) && $_GET['format'] == 'rss' ) {
		
		include( realpath( dirname( __FILE__ ) ).'/details_rss.php' );
		render_page_rss();
		
	} else {
		
		$prev_filters = prev_period( $filters );
		if ( $ajax_request || !$ajax_capable ) {
			$curr_data = load_data( $filters );
			$prev_data = load_data( $prev_filters );
		} else {
			$curr_data = array();
			$prev_data = array();
		}
		
		if ( ( !array_key_exists( 'QUERY_STRING', $_SERVER ) || $_SERVER['QUERY_STRING'] == '' ) &&
		     ( array_key_exists( 'yr', $curr_data ) && array_sum( $curr_data['yr'] ) == 0 ) &&
		     ( array_key_exists( 'yr', $prev_data ) && array_sum( $prev_data['yr'] ) == 0 ) ) {
			include( realpath( dirname( __FILE__ ) ).'/welcome.php' );
			exit;
		} else {
			include( realpath( dirname( __FILE__ ) ).'/details_html.php' );
			render_page_html();
		}
		
	}
}




// functions

function load_cache_data( $_filters ) {
	global $config, $connection, $hit_fields, $visit_fields;
	
	$query = 'SELECT `cache` FROM `'.SlimStat::esc( $config->db_database ).'`.`'.SlimStat::esc( $config->tbl_cache ).'`';
	$query .= ' WHERE `tz`=\''.SlimStat::esc( $config->timezone ).'\' AND `yr`=\''.SlimStat::esc( $_filters['yr'] ).'\'';
	$query .= ' AND `mo`=\''.SlimStat::esc( $_filters['mo'] ).'\'';
	if ( array_key_exists( 'dy', $_filters ) ) {
		$query .= ' AND `dy`=\''.SlimStat::esc( $_filters['dy'] ).'\'';
	} else {
		$query .= ' AND `dy`=\'0\'';
	}
	$query .= ' AND `app_version`=\''.SlimStat::esc( SlimStat::app_version() ).'\'';
	foreach ( $hit_fields as $key ) {
		if ( array_key_exists( $key, $_filters ) ) {
			$query .= ' AND `'.SlimStat::esc( $key ).'`=\''.SlimStat::esc( urldecode( $_filters[$key] ) ).'\'';
		} else {
			$query .= ' AND `'.SlimStat::esc( $key ).'` IS NULL';
		}
	}
	foreach ( $visit_fields as $key ) {
		if ( array_key_exists( $key, $_filters ) ) {
			$query .= ' AND `'.SlimStat::esc( $key ).'`=\''.SlimStat::esc( $_filters[$key] ).'\'';
		} elseif ( $key == 'hits' ) {
			$query .= ' AND `'.SlimStat::esc( $key ).'`=\'0\'';
		} else {
			$query .= ' AND `'.SlimStat::esc( $key ).'` IS NULL';
		}
	}
	// echo htmlspecialchars( $query )."<br />\n";
	$result = @mysql_query( $query, $connection );
	// echo htmlspecialchars( mysql_error() );
	if ( @mysql_num_rows( $result ) > 0 ) {
		list( $data_serialized ) = @mysql_fetch_row( $result );
		return unserialize( gzinflate( $data_serialized ) );
	}
	return null;
}

function get_date_clause( $_filters, $_yr_field, $_mo_field, $_dy_field, $_hr_field, $_mi_field ) {
	global $is_filtering_visits_only;
	
	if ( array_key_exists( 'dy', $_filters ) ) {
		$start_ts = mktime( 0, 0, 0, $_filters['mo'], $_filters['dy'], $_filters['yr'] );
		$end_ts = mktime( 23, 59, 59, $_filters['mo'], $_filters['dy'], $_filters['yr'] );
	} else {
		$start_ts = mktime( 0, 0, 0, $_filters['mo'], 1, $_filters['yr'] );
		$end_ts = mktime( 23, 59, 59, $_filters['mo'] + 1, 0, $_filters['yr'] );
	}

	$start_yr = intval( gmdate( 'Y', $start_ts ) );
	$start_mo = intval( gmdate( 'n', $start_ts ) );
	$start_dy = intval( gmdate( 'j', $start_ts ) );
	$start_hr = intval( gmdate( 'G', $start_ts ) );
	$start_mi = intval( floor( gmdate( 'i', $start_ts ) / 15 ) * 15 );

	$end_yr = intval( gmdate( 'Y', $end_ts ) );
	$end_mo = intval( gmdate( 'n', $end_ts ) );
	$end_dy = intval( gmdate( 'j', $end_ts ) );
	$end_hr = intval( gmdate( 'G', $end_ts ) );
	$end_mi = intval( floor( gmdate( 'i', $end_ts ) / 15 ) * 15 );
	
	$subquery = '( ( `'.$_yr_field.'`>'.$start_yr.' OR ( `'.$_yr_field.'`='.$start_yr.' AND (';
	$subquery .= ' `'.$_mo_field.'`>'.$start_mo.' OR ( `'.$_mo_field.'`='.$start_mo.' AND (';
	$subquery .= ' `'.$_dy_field.'`>'.$start_dy.' OR ( `'.$_dy_field.'`='.$start_dy.' AND (';
	$subquery .= ' `'.$_hr_field.'`>'.$start_hr.' OR ( `'.$_hr_field.'`='.$start_hr.' AND';
	$subquery .= ' `'.$_mi_field.'`>='.$start_mi.' ) ) ) ) ) ) ) )';
	$subquery .= ' AND ( `'.$_yr_field.'`<'.$end_yr.' OR ( `'.$_yr_field.'`='.$end_yr.' AND (';
	$subquery .= ' `'.$_mo_field.'`<'.$end_mo.' OR ( `'.$_mo_field.'`='.$end_mo.' AND (';
	$subquery .= ' `'.$_dy_field.'`<'.$end_dy.' OR ( `'.$_dy_field.'`='.$end_dy.' AND (';
	$subquery .= ' `'.$_hr_field.'`<'.$end_hr.' OR ( `'.$_hr_field.'`='.$end_hr.' AND';
	$subquery .= ' `'.$_mi_field.'`<='.$end_mi.' ) ) ) ) ) ) ) ) )';
	
	if ( $is_filtering_visits_only ) {
		$query = '( '.$subquery.' OR '.str_replace( 'start', 'end', $subquery ).' )';
	} else {
		$query = $subquery;
	}
	
	return $query;
}

function load_hit_data( $_filters ) {
	global $config, $connection, $is_filtering_visits_only, $time_fields, $hit_fields;
	
	if ( $is_filtering_visits_only ) {
		return array();
	}
	
	$query = 'SELECT `'.implode( '`, `', array_merge( $time_fields, $hit_fields ) ).'`, `title`, `hits` ';
	$query .= 'FROM `'.SlimStat::esc( $config->db_database ).'`.`'.SlimStat::esc( $config->tbl_hits ).'` ';
	$query .= 'WHERE ';
	
	$query .= get_date_clause( $_filters, 'yr', 'mo', 'dy', 'hr', 'mi' );
	
	foreach ( $hit_fields as $key ) {
		if ( array_key_exists( $key, $_filters ) ) {
			if ( $_filters[$key] != '' ) {
				$query .= ' AND `'.SlimStat::esc( $key ).'`=\''.SlimStat::esc( $_filters[$key] ).'\'';
			} else {
				$query .= ' AND `'.SlimStat::esc( $key ).'` IS NULL';
			}
		}
	}
	
	// echo htmlspecialchars( $query )."<br />\n";
	$result = @mysql_query( $query, $connection );
	// echo htmlspecialchars( mysql_error() );
	
	return parse_data( $result, $hit_fields, $_filters, true );
}

function load_visit_data( $_filters ) {
	global $config, $connection, $is_filtering_visits_only, $hit_fields, $visit_fields;
	
	$query = 'SELECT `start_yr`, `start_mo`, `start_dy`, `start_hr`, `'.implode( '`, `', $visit_fields );
	if ( $is_filtering_visits_only ) {
		// $query .= '`, `resource';
		$query .= '`, `'.implode( '`, `', $hit_fields ).'` ';
	} elseif ( array_key_exists( 'resource', $_filters ) ) {
		$query .= '`, `resource` AS \'visit_resource\' ';
	} else {
		$query .= '` ';
	}
	$query .= 'FROM `'.SlimStat::esc( $config->db_database ).'`.`'.SlimStat::esc( $config->tbl_visits ).'` WHERE ';
	
	$query .= get_date_clause( $_filters, 'start_yr', 'start_mo', 'start_dy', 'start_hr', 'start_mi' );
	
	foreach ( $hit_fields as $key ) {
		if ( array_key_exists( $key, $_filters ) ) {
			if ( $_filters[$key] == '' ) {
				$query .= ' AND `'.SlimStat::esc( $key ).'` IS NULL';
			} elseif ( $key == 'resource' ) {
				$query .= ' AND `'.$key.'` LIKE \'% '.SlimStat::esc( $_filters[$key] ).' %\'';
			} else {
				$query .= ' AND `'.$key.'`=\''.SlimStat::esc( $_filters[$key] ).'\'';
			}
		}
	}
	foreach ( $visit_fields as $key ) {
		if ( array_key_exists( $key, $_filters ) ) {
			if ( $_filters[$key] != '' ) {
				$query .= ' AND `'.SlimStat::esc( $key ).'`=\''.SlimStat::esc( $_filters[$key] ).'\'';
			} else {
				$query .= ' AND `'.SlimStat::esc( $key ).'` IS NULL';
			}
		}
	}
	
	// echo htmlspecialchars( $query )."<br />\n";
	$result = @mysql_query( $query, $connection );
	// echo htmlspecialchars( mysql_error() );
	if ( $is_filtering_visits_only ) {
		return parse_data( $result, array_merge( $visit_fields, $hit_fields ), $_filters, true );
	} elseif ( array_key_exists( 'resource', $_filters ) ) {
		return parse_data( $result, array_merge( $visit_fields, array( 'visit_resource' ) ), $_filters );
	} else {
		return parse_data( $result, $visit_fields, $_filters );
	}
}

function save_cache_data( $_filters, $_data ) {
	global $config, $connection, $hit_fields, $visit_fields;
	
	$query = 'INSERT INTO `'.SlimStat::esc( $config->db_database ).'`.`'.SlimStat::esc( $config->tbl_cache ).'` ';
	$query .= '( `app_version`, `tz`, `yr`, `mo`, `dy`, `'.implode( '`, `', $hit_fields );
	$query .= '`, `'.implode( '`, `', $visit_fields ).'`, `cache` ) VALUES ( \'';
	$query .= SlimStat::esc( SlimStat::app_version() ).'\', \''.SlimStat::esc( $config->timezone ).'\', ';
	foreach ( array( 'yr', 'mo', 'dy' ) as $key ) {
		if ( array_key_exists( $key, $_filters ) ) {
			$query .= '\''.SlimStat::esc( $_filters[$key] ).'\', ';
		} else {
			$query .= '\'0\', ';
		}
	}
	foreach ( $hit_fields as $key ) {
		if ( array_key_exists( $key, $_filters ) ) {
			$query .= '\''.SlimStat::esc( $_filters[$key] ).'\', ';
		} else {
			$query .= 'NULL, ';
		}
	}
	foreach ( $visit_fields as $key ) {
		if ( array_key_exists( $key, $_filters ) ) {
			$query .= '\''.SlimStat::esc( $_filters[$key] ).'\', ';
		} elseif ( $key == 'hits' ) {
			$query .= '\'0\', ';
		} else {
			$query .= 'NULL, ';
		}
	}
	$query .= '\''.SlimStat::esc( gzdeflate( serialize( $_data ) ) ).'\' )';
	// echo htmlspecialchars( mb_substr( $query, 0, 400 ) )."<br />\n";
	@mysql_query( $query, $connection );
	// echo htmlspecialchars( mysql_error() );
}

function load_data( $_filters ) {
	global $config, $i18n, $hit_fields, $visit_fields;
	
	$data = null;
	
	$have_cache_data = false;
	
	$is_caching = true;
	
	// get cached data
	if ( $is_caching && is_period_past( $_filters ) ) {
		$data = load_cache_data( $_filters );
		$have_cache_data = is_array( $data );
	}
	
	// if data is not cached, get data manually
	if ( !$have_cache_data ) {
		if ( array_key_exists( 'dy', $_filters ) ) { // day
			$data = array_merge( load_hit_data( $_filters ), load_visit_data( $_filters ) );
		} else { // month	
			$data = array();
			$max_dy = valid_dy( 31, $_filters['mo'], $_filters['yr'] );
			for ( $dy = 1; $dy <= $max_dy; $dy++ ) {
				$dy_data = load_data( array_merge( $_filters, array( 'dy' => $dy ) ) );
				
				foreach ( $dy_data as $field => $field_data ) {
					if ( !array_key_exists( $field, $data ) ) {
						$data[$field] = array();
					}
					if ( $field == 'title' ) {
						$data[$field] = array_merge( $data[$field], $field_data );
					} elseif ( $field == 'version' ) {
						foreach ( $field_data as $browser => $version_data ) {
							if ( !array_key_exists( $browser, $data[$field] ) ) {
								$data[$field][$browser] = array();
							}
							foreach ( $version_data as $key => $value ) {
								if ( array_key_exists( $key, $data[$field][$browser] ) ) {
									$data[$field][$browser][$key] += $value;
								} else {
									$data[$field][$browser][$key] = $value;
								}
							}
						}
					} else {
						foreach ( $field_data as $key => $value ) {
							if ( array_key_exists( $key, $data[$field] ) ) {
								$data[$field][$key] += $value;
							} else {
								$data[$field][$key] = $value;
							}
						}
					}
				}
			}
			
			sort_data( $data, array_merge( $hit_fields, $visit_fields ), true );
		}
	}
	
	if ( array_key_exists( 'title', $data ) ) {
		foreach ( $data['title'] as $key => $value ) {
			$i18n->data['labels']['resource.'.$key] = $value;
		}
	}
	
	// save data to cache
	if ( $is_caching && is_period_past( $_filters ) && !$have_cache_data ) {
		save_cache_data( $_filters, $data );
	}
	
	return $data;
}

function parse_data( $_result, $_fields, $_filters, $_init_time_fields=false ) {
	global $config, $is_filtering_visits_only, $time_fields, $hit_fields, $visit_fields;
	
	$data = array();
	
	foreach ( array_merge( $_fields, array( 'prev_resource', 'next_resource' ) ) as $field ) {
		$data[$field] = array();
	}
	$data['source'] = array( 'search_terms' => 0, 'referrer' => 0, 'direct' => 0 );
	
	// echo '<pre>';
	
	while ( $row = @mysql_fetch_assoc( $_result ) ) {
		if ( $is_filtering_visits_only ) {
			$local_start_time = SlimStat::local_time_fields( array( 'yr' => $row['start_yr'], 'mo' => $row['start_mo'], 'dy' => $row['start_dy'], 'hr' => $row['start_hr'], 0, 0 ) );
			
			$values = explode( "\n", $row['resource'] );
			$hits = 0;
			$valid_values = '';
			foreach ( $values as $value ) {
				if ( $value == '' ) {
					continue;
				}
				
				@list( $yr, $mo, $dy, $hr, $mi, $sc, $resource, $title ) = explode( ' ', $value, 8 );
				$local_time = SlimStat::local_time_fields( array( 'yr' => $yr, 'mo' => $mo, 'dy' => $dy, 'hr' => $hr, 'mi' => $mi, 'sc' => $sc ) );
				
				if ( ( array_key_exists( 'yr', $_filters ) && $local_time['yr'] != $_filters['yr'] ) ||
				     ( array_key_exists( 'mo', $_filters ) && $local_time['mo'] != $_filters['mo'] ) ||
				     ( array_key_exists( 'dy', $_filters ) && $local_time['dy'] != $_filters['dy'] ) ||
				     ( array_key_exists( 'hr', $_filters ) && $local_time['hr'] != $_filters['hr'] ) ||
				     ( array_key_exists( 'resource', $_filters ) && $resource != $_filters['resource'] ) ) {
					continue;
				}
				
				$valid_values .= $local_time['yr'].' '.$local_time['mo'].' '.$local_time['dy'].' ';
				$valid_values .= $local_time['hr'].' '.$local_time['mi'].' '.$local_time['sc'].' '.$resource.' '.$title."\n";
				
				$hits++;
			}
			if ( $hits == 0 ) {
				continue;
			} else {
				$row['resource'] = $valid_values;
				$row['hits'] = $hits;
			}
		}
		
		// print_r( $row );
		
		// convert to local time
		if ( array_key_exists( 'yr', $row ) && array_key_exists( 'mo', $row ) &&
		     array_key_exists( 'dy', $row ) && array_key_exists( 'hr', $row ) ) {
			$local_time = SlimStat::local_time_fields( $row );
			foreach ( $local_time as $field => $value ) {
				if ( !array_key_exists( $field, $data ) ) {
					$data[$field] = array();
				}
			
				if ( array_key_exists( $value, $data[$field] ) ) {
					$data[$field][$value] += $row['hits'];
				} else {
					$data[$field][$value] = $row['hits'];
				}
			}
		}
		
		if ( array_key_exists( 'title', $row ) && $row['title'] != '' && array_key_exists( 'resource', $row ) ) {
			if ( !array_key_exists( 'title', $data ) ) {
				$data['title'] = array();
			}
			$data['title'][ $row['resource'] ] = $row['title'];
		}
		
		if ( array_key_exists( 'search_terms', $row ) && array_key_exists( 'referrer', $row ) ) {
			if ( $row['search_terms'] != '' ) {
				$data['source']['search_terms']++;
			} elseif ( $row['referrer'] != '' ) {
				$data['source']['referrer']++;
			} else {
				$data['source']['direct']++;
			}
		}
		
		foreach ( $_fields as $field ) {
			if ( !array_key_exists( $field, $row ) ) {
				continue;
			}
			
			$value = $row[$field];
			
			if ( ( $field == 'search_terms' || $field == 'referrer' || $field == 'domain' ) && $value == '' ) {
				continue;
			}
			
			if ( $field == 'version' ) {
				$browser = $row['browser'];
				if ( !array_key_exists( $browser, $data[$field] ) ) {
					$data[$field][$browser] = array();
				}
				
				if ( $is_filtering_visits_only ) {
					if ( array_key_exists( $value, $data[$field][$browser] ) ) {
						$data[$field][$browser][$value]++;
					} else {
						$data[$field][$browser][$value] = 1;
					}
				} else {
					if ( array_key_exists( $value, $data[$field][$browser] ) ) {
						$data[$field][$browser][$value] += $row['hits'];
					} else {
						$data[$field][$browser][$value] = $row['hits'];
					}
				}
			} elseif ( $field == 'resource' && $is_filtering_visits_only ) {
				if ( array_key_exists( 'start_resource', $_filters ) ) {
					parse_next_prev_resources( $data, $value, $_filters['start_resource'] );
				} elseif ( array_key_exists( 'end_resource', $_filters ) ) {
					parse_next_prev_resources( $data, $value, $_filters['end_resource'] );
				}
				
				$values = explode( "\n", $value );
				$resources = array();
				foreach ( $values as $value ) {
					if ( $value == '' ) {
						continue;
					}
					
					// time was converted to local time above, no need to convert again
					list( $yr, $mo, $dy, $hr, $mi, $sc, $resource, $title ) = explode( ' ', $value, 8 );
					
					if ( $title != '' ) {
						if ( !array_key_exists( 'title', $data ) ) {
							$data['title'] = array();
						}
						$data['title'][$resource] = $title;
					}
					
					$hit_time_fields = array( 'yr' => $yr, 'mo' => $mo, 'dy' => $dy, 'hr' => $hr );
					
					foreach ( $hit_time_fields as $time_field => $time_value ) {
						if ( !array_key_exists( $time_field, $data ) ) {
							$data[$time_field] = array();
						}
						if ( array_key_exists( $time_value, $data[$time_field] ) ) {
							$data[$time_field][$time_value]++;
						} else {
							$data[$time_field][$time_value] = 1;
						}
					}
					
					if ( $resource != '' ) {
						if ( array_key_exists( $resource, $data[$field] ) ) {
							$data[$field][$resource]++;
						} else {
							$data[$field][$resource] = 1;
						}
						$resources[] = $resource;
					}
				}
			} elseif ( $field == 'visit_resource' ) {
				parse_next_prev_resources( $data, $value, $_filters['resource'] );
			} elseif ( in_array( $field, $visit_fields ) || $is_filtering_visits_only ) {
				if ( $is_filtering_visits_only &&
					 ( ( array_key_exists( 'yr', $_filters ) && $local_start_time['yr'] != $_filters['yr'] ) ||
				       ( array_key_exists( 'mo', $_filters ) && $local_start_time['mo'] != $_filters['mo'] ) ||
				       ( array_key_exists( 'dy', $_filters ) && $local_start_time['dy'] != $_filters['dy'] ) ||
				       ( array_key_exists( 'hr', $_filters ) && $local_start_time['hr'] != $_filters['hr'] ) ) ) {
					continue;
				}
				
				if ( array_key_exists( $value, $data[$field] ) ) {
					$data[$field][$value]++;
				} else {
					$data[$field][$value] = 1;
				}
			} else {
				if ( array_key_exists( $value, $data[$field] ) ) {
					$data[$field][$value] += $row['hits'];
				} else {
					$data[$field][$value] = $row['hits'];
				}
			}
		}
	}
	
	sort_data( $data, $_fields, $_init_time_fields );
	
	// echo '</pre>'."\n";
	
	return $data;
}

function sort_data( &$_data, $_fields, $_init_time_fields=false ) {
	global $time_fields;
	
	foreach ( $time_fields as $field ) {
		if ( array_key_exists( $field, $_data ) ) {
			ksort( $_data[$field] );
		} elseif ( $_init_time_fields ) {
			$_data[$field] = array();
		}
	}
	foreach ( array_merge( $_fields, array( 'prev_resource', 'next_resource' ) ) as $field ) {
		if ( array_key_exists( $field, $_data ) ) {
			if ( $field == 'version' ) {
				foreach ( array_keys( $_data[$field] ) as $browser ) {
					arsort( $_data[$field][$browser] );
				}
			} else {
				arsort( $_data[$field] );
			}
		}
	}
}

function parse_next_prev_resources( &$_data, &$_visit_resource, &$_resource ) {
	$visit_resources = explode( "\n", $_visit_resource );
	$resources = array();
	foreach ( $visit_resources as $visit_resource ) {
		if ( $visit_resource == '' ) {
			continue;
		}

		list( $yr, $mo, $dy, $hr, $mi, $sc, $resource, $title ) = explode( ' ', $visit_resource, 8 );
		
		if ( $title != '' ) {
			if ( !array_key_exists( 'title', $_data ) ) {
				$_data['title'] = array();
			}
			$_data['title'][$resource] = $title;
		}
		
		if ( $resource != '' ) {
			$resources[] = $resource;
		}
	}
	
	for ( $resource_pos=0; $resource_pos<sizeof( $resources ); $resource_pos++ ) {
		if ( $resources[$resource_pos] == $_resource ) {
			$prev_resource = '';
			$next_resource = '';
			if ( $resource_pos > 0 ) {
				$prev_resource = $resources[$resource_pos - 1];
			}
			if ( $resource_pos < sizeof( $resources ) - 1 ) {
				$next_resource = $resources[$resource_pos + 1];
			}

			if ( array_key_exists( $prev_resource, $_data['prev_resource'] ) ) {
				$_data['prev_resource'][$prev_resource]++;
			} else {
				$_data['prev_resource'][$prev_resource] = 1;
			}
			if ( array_key_exists( $next_resource, $_data['next_resource'] ) ) {
				$_data['next_resource'][$next_resource]++;
			} else {
				$_data['next_resource'][$next_resource] = 1;
			}
		}
	}
}

function is_period_past( $_filters ) {
	return (
		$_filters['yr'] < gmdate( 'Y' ) ||
		$_filters['mo'] < gmdate( 'n' ) ||
		( array_key_exists( 'dy', $_filters ) && $_filters['dy'] < gmdate( 'j' ) ) );
}

function valid_hr( $_hr ) {
	return max( 0, min( 23, intval( $_hr ) ) );
}

function valid_dy( $_dy, $_mo, $_yr ) {
	$dy = max( 1, min( date( 'j', gmmktime( 12, 0, 0, $_mo + 1, 0, $_yr ) ), intval( $_dy ) ) );
	if ( $_yr == date( 'Y' ) && $_mo == date( 'n' ) ) {
		$dy = min( date( 'j' ), $dy );
	}
	return $dy;
}

function valid_mo( $_mo ) {
	return max( 1, min( 12, intval( $_mo ) ) );
}

function valid_yr( $_yr ) {
	return max( 1970, min( 3000, intval( $_yr ) ) );
}

function date_label( $_array, $_dy_override=null ) {
	$yr = valid_yr( $_array['yr'] );
	$mo = ( array_key_exists( 'mo', $_array ) ) ? valid_mo( $_array['mo'] ) : null;
	$dy = ( array_key_exists( 'dy', $_array ) ) ? valid_dy( $_array['dy'], $mo, $yr ) : null;
	if ( $_dy_override === false ) {
		$dy = null;
	} elseif ( $_dy_override > 0 ) {
		$dy = valid_dy( $_dy_override, $mo, $yr );
	}
	
	if ( $dy != null && $mo != null ) {
		// echo gmstrftime( '%z', gmmktime( 12, 0, 0, $mo, $dy, $yr ) );
		return gmstrftime( '%a %e %b %Y', gmmktime( 12, 0, 0, $mo, $dy, $yr ) );
	} elseif ( $mo != null ) {
		// echo gmstrftime( '%z', gmmktime( 12, 0, 0, $mo, 1, $yr ) );
		return gmstrftime( '%b %Y', gmmktime( 12, 0, 0, $mo, 1, $yr ) );
	} else {
		return $yr;
	}
}

function prev_period( $_query_fields, $_ignore_dy=false ) {
	$prev_fields = $_query_fields;
	
	if ( $_ignore_dy && array_key_exists( 'dy', $prev_fields ) ) {
		unset( $prev_fields['dy'] );
	}
	
	if ( !$_ignore_dy && array_key_exists( 'dy', $_query_fields ) && array_key_exists( 'mo', $_query_fields ) && array_key_exists( 'yr', $_query_fields ) ) {
		$prev_ts = gmmktime( 12, 0, 0, $_query_fields['mo'], $_query_fields['dy'] - 1, $_query_fields['yr'] );
		$prev_fields['dy'] = date( 'j', $prev_ts );
		$prev_fields['mo'] = date( 'n', $prev_ts );
		$prev_fields['yr'] = date( 'Y', $prev_ts );
	} elseif ( array_key_exists( 'mo', $_query_fields ) && array_key_exists( 'yr', $_query_fields ) ) {
		$prev_ts = gmmktime( 12, 0, 0, $_query_fields['mo'] - 1, 1, $_query_fields['yr'] );
		$prev_fields['mo'] = date( 'n', $prev_ts );
		$prev_fields['yr'] = date( 'Y', $prev_ts );
	} elseif ( array_key_exists( 'yr', $_query_fields ) ) {
		$prev_fields['yr'] = $_query_fields['yr'] - 1;
	}
	
	return $prev_fields;
}

function next_period( $_query_fields, $_ignore_dy=false ) {
	$next_fields = $_query_fields;
	
	if ( $_ignore_dy && array_key_exists( 'dy', $next_fields ) ) {
		unset( $next_fields['dy'] );
	}

	if ( !$_ignore_dy && array_key_exists( 'dy', $_query_fields ) && array_key_exists( 'mo', $_query_fields ) && array_key_exists( 'yr', $_query_fields ) ) {
		$next_ts = gmmktime( 12, 0, 0, $_query_fields['mo'], $_query_fields['dy'] + 1, $_query_fields['yr'] );
		$next_fields['dy'] = date( 'j', $next_ts );
		$next_fields['mo'] = date( 'n', $next_ts );
		$next_fields['yr'] = date( 'Y', $next_ts );
	} elseif ( array_key_exists( 'mo', $_query_fields ) && array_key_exists( 'yr', $_query_fields ) ) {
		$next_ts = gmmktime( 12, 0, 0, $_query_fields['mo'] + 1, 1, $_query_fields['yr'] );
		$next_fields['mo'] = date( 'n', $next_ts );
		$next_fields['yr'] = date( 'Y', $next_ts );
	} elseif ( array_key_exists( 'yr', $_query_fields ) ) {
		$next_fields['yr'] = $_query_fields['yr'] + 1;
	}

	return $next_fields;
}

function format_percent( $_percent ) {
	if ( $_percent < 100 ) {
		return format_number( $_percent );
	} else {
		return round( $_percent );
	}
}

function to1dp( $_number ) {
	return number_format( $_number, 1, '.', '' );
}

function hsc( $_str ) {
	return htmlspecialchars( $_str );
}
Return current item: Banshee PHP Framework