<?php
class ADODB_Pager {
	var $id; 	// unique id for pager (defaults to 'adodb')
	var $db; 	// ADODB connection object
	var $sql; 	// sql used
	var $rs;	// recordset generated
	var $curr_page;	// current page number before Render() called, calculated in constructor
	var $rows;		// number of rows per page
	var $linksPerPage=10; // number of links per page in navigation bar
	var $page_file;
	var $pager_grid_formid = 'pagergrid_form';
	var $pager_tab_file = 'pager_tab.php';
	var $javascript_row_click_action = true; // im listing die rows klickbar
	var $showPageLinks; 
	var $index_field;
	var $get_row_selector; // GET PARAM FOR SELECTING A ROW
	var $selected_row;
	var $gridAttributes = 'class="pagerresults"';
	var $dateFormat = 'd.m.Y';
	var $sortOrders;
	var $SortingInput;
	var $SearchInput;
	var $hide_cols = array(); // hide columns from output
	var $additional_get_params = array();
	var $searchFields = array();
	var $whereConditions = array();
	var $groupbyConditions = array();
	var $orderbyDefault = array();
	
	var $get_array_exclude = array('where_filter','search','sort_field','sort_direction');
	
	// Localize text strings here
	var $first = '<code>|&lt;</code>';
	var $prev = '<code>&lt;&lt;</code>';
	var $next = '<code>>></code>';
	var $last = '<code>>|</code>';
	var $moreLinks = '...';
	var $startLinks = '...';
	var $gridHeader = false;
	var $gridHelper = false;
	var $htmlSpecialChars = true;
	var $page = TEXT_RESULT_PAGE;
	var $linkSelectedColor = '#FFFFFF';
	var $linkCssClass = 'pagernavigation';
	var $cache = 0;  #secs to cache with CachePageExecute()
	var $in_div_links = false;
	var $in_div_div_id = '';

	// DONT DISPLAY -> RETURN IT
	var $Header;
	var $Grid;
	var $Footer;
	
	var $where_filter;
	
	//----------------------------------------------
	// constructor
	//
	// $db	adodb connection object
	// $sql	sql statement
	// $id	optional id to identify which pager, 
	//		if you have multiple on 1 page. 
	//		$id should be only be [a-z0-9]*
	//
	function ADODB_Pager(&$db,$sql,$id = 'adodb', $showPageLinks = false)
	{
		global $PHP_SELF;

		$curr_page = $id.'_curr_page';
		if (empty($PHP_SELF)) $PHP_SELF = htmlspecialchars($_SERVER['PHP_SELF']); // htmlspecialchars() to prevent XSS attacks

		$this->page_file = yes_get_php_self();
		
		$this->sql = $sql;

		$this->id = $id;
		$this->db = $db;
		$this->showPageLinks = $showPageLinks;
		
		$next_page = $id.'_next_page';	
		
		// KEIN SESSIONHANDLING VERWENDEN!
		/*
		if (isset($_GET[$next_page])) {
			$_SESSION[$curr_page] = (integer) $_GET[$next_page];
		}
		if (empty($_SESSION[$curr_page])) $_SESSION[$curr_page] = 1; ## at first page
		
		$this->curr_page = $_SESSION[$curr_page];
		*/
		$this->curr_page = ($_GET[$next_page]) ? $_GET[$next_page] : 1;
		
	}
	
	function generate_sql(){
		if($_REQUEST['where_filter']){
			$this->whereConditions[] = $this->where_filter[0]."='".$_REQUEST['where_filter']."' ";
		}else{
			if($this->where_filter){
				$this->whereConditions[] = $this->where_filter[0]."='".$this->where_filter[3]."' ";
			}
		}
		if($_REQUEST['search'] and strlen(trim($_REQUEST['search']))>0 and sizeOf($this->searchFields)>0){
			$search = xtc_db_input($_REQUEST['search']);
			$search_where = " ( ";
			foreach($this->searchFields as $k=>$searchfield){
				$search_where .= $searchfield." LIKE '%".$search."%' OR ";
			}
			$search_where = substr($search_where,0,strlen($search_where)-3);
			$search_where .= " ) ";
			$this->whereConditions[] = $search_where;
		}
		
		if($_REQUEST['sort_field']){
			$this->orderbyDefault[] = $_REQUEST['sort_field']." ".$_REQUEST['sort_direction'];
		}
		
		if(sizeOf($this->whereConditions)>0){
			$conditions = array_reverse($this->whereConditions);
			$this->sql .= ' WHERE ';
			$this->sql .= implode(' AND ',$conditions);
		}
		if(sizeOf($this->groupbyConditions)>0){
			$groupby =array_reverse($this->groupbyConditions);
			$this->sql .= ' GROUP BY ';
			$this->sql .= implode(',',$groupby);
		}
		if(sizeOf($this->orderbyDefault)>0){
			$orderby =array_reverse($this->orderbyDefault);
			$this->sql .= ' ORDER BY ';
			$this->sql .= implode(',',$orderby);
		}
	}
	
	//---------------------------
	// Display link to first page
	function Render_First($anchor=true)
	{
		if ($anchor) {
			$excl = array_merge($this->get_array_exclude,array('adodb_next_page'));
			echo $this->make_link($this->first,xtc_get_all_get_params($excl).'adodb_next_page=1'.$this->get_params_for_navigation());
		} else {
			print "$this->first &nbsp; ";
		}
	}
	
	//--------------------------
	// Display link to next page
	function render_next($anchor=true)
	{
		if ($anchor) {
			$excl = array_merge($this->get_array_exclude,array('adodb_next_page'));
			echo $this->make_link($this->next,xtc_get_all_get_params($excl).'adodb_next_page='.($this->rs->AbsolutePage() + 1).$this->get_params_for_navigation());
		} else {
			print "$this->next &nbsp; ";
		}
	}
	
	//------------------
	// Link to last page
	// 
	// for better performance with large recordsets, you can set
	// $this->db->pageExecuteCountRows = false, which disables
	// last page counting.
	function render_last($anchor=true)
	{
		if (!$this->db->pageExecuteCountRows) return;
		if ($anchor) {
			$excl = array_merge($this->get_array_exclude,array('adodb_next_page'));
			echo $this->make_link($this->last, xtc_get_all_get_params($excl).'adodb_next_page='.$this->rs->LastPageNo().$this->get_params_for_navigation() );
		} else {
			print "$this->last &nbsp; ";
		}
	}
	
	//---------------------------------------------------
	// original code by "Pablo Costa" <pablo@cbsp.com.br> 
        function render_pagelinks()
        {
            $pages        = $this->rs->LastPageNo();
            $linksperpage = $this->linksPerPage ? $this->linksPerPage : $pages;
            for($i=1; $i <= $pages; $i+=$linksperpage)
            {
                if($this->rs->AbsolutePage() >= $i)
                {
                    $start = $i;
                }
            }
			$numbers = '';
            $end = $start+$linksperpage-1;
			$link = $this->id . "_next_page";
            if($end > $pages) $end = $pages;
			
			
			if ($this->startLinks && $start > 1) {
				$pos = $start - 1;
				$excl = array_merge($this->get_array_exclude,array('adodb_next_page'));
				$numbers .= $this->make_link($this->startLinks,xtc_get_all_get_params($excl).'adodb_next_page='.$pos.$this->get_params_for_navigation());
            } 
			
			$excl = array_merge($this->get_array_exclude,array('adodb_next_page'));
            for($i=$start; $i <= $end; $i++) {
                if ($this->rs->AbsolutePage() == $i)
                    $numbers .= "<font color=$this->linkSelectedColor><b>$i</b></font>  ";
                else
                	$numbers .= $this->make_link($i,xtc_get_all_get_params($excl).'adodb_next_page='.$i.$this->get_params_for_navigation());
            }
			if ($this->moreLinks && $end < $pages)
				$numbers .= $this->make_link(xtc_get_all_get_params($excl).'adodb_next_page='.$i,$this->moreLinks.$this->get_params_for_navigation());
            print $numbers . ' &nbsp; ';
        }
	// Link to previous page
	function render_prev($anchor=true)
	{
		if ($anchor) {
			$excl = array_merge($this->get_array_exclude,array('adodb_next_page'));
			echo $this->make_link($this->prev,xtc_get_all_get_params($excl).'adodb_next_page='.($this->rs->AbsolutePage() - 1).$this->get_params_for_navigation());
		} else {
			print "$this->prev &nbsp; ";
		}
	}
	
	//--------------------------------------------------------
	// Simply rendering of grid. You should override this for
	// better control over the format of the grid
	//
	// We use output buffering to keep code clean and readable.
	function RenderGrid($smarty = true)
	{
		$gSQLBlockRows = $this->rows;
		$smarty_array = $this->rs2html();
		$smarty_array['pager_grid_formid'] = $this->pager_grid_formid;
		$smarty_array['select_all'] = TEXT_SELECT_ALL;
		$smarty_array['deselect_all'] = TEXT_DESELECT_ALL;
		return $smarty_array;
	}
	
	//-------------------------------------------------------
	// Navigation bar
	//
	// we use output buffering to keep the code easy to read.
	function RenderNav()
	{
		ob_start();
		if (!$this->rs->AtFirstPage()) {
			$this->Render_First();
			$this->Render_Prev();
		} else {
			$this->Render_First(false);
			$this->Render_Prev(false);
		}
        if ($this->showPageLinks){
            $this->Render_PageLinks();
        }
		if (!$this->rs->AtLastPage()) {
			$this->Render_Next();
			$this->Render_Last();
		} else {
			$this->Render_Next(false);
			$this->Render_Last(false);
		}
		echo ' (Total: '.($this->rs->_maxRecordCount).')';
		$s = ob_get_contents();
		ob_end_clean();
		return $s;
	}
	
	//-------------------
	// This is the footer
	function RenderPageCount()
	{
		if (!$this->db->pageExecuteCountRows) return '';
		$lastPage = $this->rs->LastPageNo();
		if ($lastPage == -1) $lastPage = 1; // check for empty rs.
		if ($this->curr_page > $lastPage) $this->curr_page = 1;
		return sprintf($this->page,$this->curr_page,$lastPage);
	}
	
	//-----------------------------------
	// Call this class to draw everything.
	function Render($rows=10)
	{
		global $ADODB_COUNTRECS;
		$this->generate_sql();
		
		$this->rows = $rows;
		
		if ($this->db->dataProvider == 'informix') $this->db->cursorType = IFX_SCROLL;

		$savec = $ADODB_COUNTRECS;
		if ($this->db->pageExecuteCountRows) $ADODB_COUNTRECS = true;
		if ($this->cache)
			$rs = &$this->db->CachePageExecute($this->cache,$this->sql,$rows,$this->curr_page);
		else
			$rs = &$this->db->PageExecute($this->sql,$rows,$this->curr_page);
		$ADODB_COUNTRECS = $savec;
		
		$this->rs = &$rs;
		if (!$rs) {
			print "<h3>Query failed: $this->sql</h3>";
			return;
		}
		
		if (!$rs->EOF && (!$rs->AtFirstPage() || !$rs->AtLastPage())) 
			$header = $this->RenderNav();
		else
			$header = "&nbsp;";

		$this->Header = $header;
		
		$this->Grid = $this->RenderGrid();
		$this->Footer = $this->RenderPageCount();
		$this->SortingInput = $this->RenderSortOrders();
		$this->SearchInput = $this->RenderSearchFields();
		$this->where_filter = $this->RenderWhereFilter();
		
// DONT DISPLAY -> RETURN
//		$this->RenderLayout($header,$grid,$footer);
		
		$rs->Close();
		$this->rs = false;
	}

	function get_params_for_navigation(){
		$strings = array();
		for($i=0;$i<sizeOf($this->get_array_exclude);$i++){
			if($_REQUEST[$this->get_array_exclude[$i]]){
				$strings[] = $this->get_array_exclude[$i].'='.$_REQUEST[$this->get_array_exclude[$i]];
			}
		}
		if(sizeOf($strings)>0){
			return '&'.implode('&',$strings);
		}
	}
	
	function get_params($from){
		$string = '';
		switch($from){
			case 'where_filter':
				if($_POST['sort_field'] or $_GET['sort_field'])
					$string .= '&sort_field='.$_REQUEST['sort_field'];
				if($_POST['sort_direction'] or $_GET['sort_direction'])
					$string .= '&sort_direction='.$_REQUEST['sort_direction'];
				if(($_POST['search'] and strlen(trim($_POST['search']))>0)  or ($_GET['search'] and strlen(trim($_GET['search']))>0) )
					$string .= '&search='.$_REQUEST['search'];
				return $string;
				break;
			case 'search':
				if($_POST['sort_field'] or $_GET['sort_field'])
					$string .= '&sort_field='.$_REQUEST['sort_field'];
				if($_POST['sort_direction'] or $_GET['sort_direction'])
					$string .= '&sort_direction='.$_REQUEST['sort_direction'];
				if($_POST['where_filter'] or $_GET['where_filter'])
					$string .= '&where_filter='.$_REQUEST['where_filter'];
				break;
			default:
				if($_POST['where_filter'] or $_GET['where_filter'])
					$string .= '&where_filter='.$_REQUEST['where_filter'];
				if(($_POST['search'] and strlen(trim($_POST['search'])>0))  or ($_GET['search'] and strlen(trim($_GET['search']))>0) )
					$string .= '&search='.$_REQUEST['search'];
				break;
		}
		$additional = $this->get_additional_get_params();
		if(sizeOf($additional)>0){
			$add = implode("&",$additional);
			if(strlen($string)>0)
				$string .= '&'.$add;
			else
				$string .= $add;
		}
		if($_GET['adodb_next_page']){
			$string .= '&adodb_next_page='.$_GET['adodb_next_page'];
		}
		return $string;
	}
	function RenderWhereFilter(){
		if(!$this->where_filter)
			return false;
		$string = xtc_draw_form('where_filter',$this->page_file,$this->get_params('where_filter'),'POST');
		$options = $this->where_filter[2]; 
		$string .= $this->where_filter[1].'&nbsp;'.xtc_draw_pull_down_menu('where_filter',$options,$_REQUEST['where_filter'],'onchange="this.form.submit();" class="pagerinput"');
		$string .= '</form>';
		return $string;
	}
	function RenderSortOrders(){
		if(!$this->sortOrders)
			return false;
		$options = array();
		foreach($this->sortOrders as $id=>$text){
			$options[] = array('id'=>$id,'text'=>$text);
		}
		$string  = xtc_draw_form('sort',$this->page_file,$this->get_params('sort'),'POST');
		$string .= TEXT_SORT_RESULT.'&nbsp;'.xtc_draw_pull_down_menu('sort_field',$options,$_REQUEST['sort_field'],'onchange="this.form.submit();" class="pagerinput"');
		$string .= xtc_draw_pull_down_menu('sort_direction',array(0=>array('id'=>'ASC','text'=>TEXT_SORT_ASC),1=>array('id'=>'DESC','text'=>TEXT_SORT_DESC)),$_REQUEST['sort_direction'],'onchange="this.form.submit();" class="pagerinput"');
		$string .= '</form>';
		return $string;
	}
	function RenderSearchFields(){
		if(!$this->searchFields)
			return false;
		$string = '<table border="0" cellspacing="0" cellpadding="2"><tr><td class="pagerSort">';
		$string .= xtc_draw_form('search',$this->page_file,$this->get_params('search'),'POST');
		$string .= IMAGE_SEARCH.'&nbsp;'.xtc_draw_input_field('search',$_REQUEST['search'],'class="pagerinput"');
		$string .= xtc_draw_input_field('submit',IMAGE_SEARCH,'class="iButton"',false,'submit');
		$string .= '</form></td>';
		if(is_array($this->searchFields) and sizeOf($this->searchFields)>0){
			$alt_tag = TEXT_SEARCHFIELDS.implode(', ',$this->searchFields);
			$string .= '<td class="main">'.xtc_image('images/icons/help.gif?'.microtime(),$alt_tag).'</td>';
		}
		$string .= '</tr></table>';
		return $string;
	}
	
	//------------------------------------------------------
	// override this to control overall layout and formating
	function RenderLayout($header,$grid,$footer,$attributes='border=1 bgcolor=beige')
	{
		echo "<table ".$attributes."><tr><td>",
				$header,
			"</td></tr><tr><td>",
				$grid,
			"</td></tr><tr><td>",
				$footer,
			"</td></tr></table>";
	}

	function getHeader(){
		return $this->Header;
	}
	function getFooter(){
		return $this->Footer;
	}
	function getGrid(){
		return $this->Grid;
	}
	function getSortingInput(){
		return $this->SortingInput;
	}
	function getSearchInput(){
		return $this->SearchInput;
	}
	function getWhereFilter(){
		return $this->where_filter;
	}

	
	function add_column_to_rowset($column_name,$column_value){
		$visible = 	(!in_array($column_name,$this->hide_cols)) ? true : false;
		$row = array(
			'value'=>$column_value,
			'visible'=>$visible
		);
		return $row;
	}
	

	function rs2html(){
		$rows=0;
		$docnt = false;
		global $gSQLMaxRows,$gSQLBlockRows,$ADODB_ROUND;
	
		$smarty_array = array();
		if (!$this->gridAttributes) 
			$this->gridAttributes = "BORDER='1' WIDTH='98%'";

		$smarty_array['ztabhtml'] = $this->gridAttributes;

		//else $docnt = true;
		$typearr = array();
		$ncols = $this->rs->FieldCount();

		$smarty_array['ncols'] = $ncols;

		$header_items = array(); // SMARTY ADDON
		$matched_header_count = 0;
		for($i=0; $i < $ncols; $i++) {	
			$field = $this->rs->FetchField($i);
			if ($field) {
					if ($this->gridHeader) $fname = $this->gridHeader[$matched_header_count];
					else $fname = htmlspecialchars($field->name);
					$typearr[$i] = $this->rs->MetaType($field->type,$field->max_length);
			} else {
					$fname = 'Field '.($i+1);
					$typearr[$i] = 'C';
			}
			if (strlen($fname)==0) $fname = '&nbsp;';
			if(!in_array($field->name,$this->hide_cols)){
				$header_items[] = $fname;
				$matched_header_count++;
			}
		}
		$smarty_array['header_items'] = $header_items;
		$smarty_array['index_field'] = $this->index_field;
		$smarty_array['get_row_selector'] = $this->get_row_selector;

		$content_rows = array(); // SMARTY ADDON

		$selected_row = false;

		// smart algorithm - handles ADODB_FETCH_MODE's correctly by probing...
		$numoffset = isset($this->rs->fields[0]) ||isset($this->rs->fields[1]) || isset($this->rs->fields[2]);
		while (!$this->rs->EOF) {
			$my_row = array();

			for ($i=0; $i < $ncols; $i++) {
				if ($i===0) $v=($numoffset) ? $this->rs->fields[0] : reset($this->rs->fields);
				else $v = ($numoffset) ? $this->rs->fields[$i] : next($this->rs->fields);

				$column = key( $this->rs->fields );
				
					if($this->index_field == $column)
						$index_id = $v;
	
					// GET VALUE FROM USERFUNC
					if( $this->gridHelper and $this->gridHelper[$column] ){
						if( function_exists($this->gridHelper[$column]) ){
							$v = call_user_func($this->gridHelper[$column],$v);
							$my_row[] = $this->add_column_to_rowset($column,$v);
						}
					}else{
					// GET DB VALUE
						$type = $typearr[$i];
						switch($type) {
						case 'D':
							if (empty($v)) {
								$my_row[] = $this->add_column_to_rowset($column,'&nbsp;');
							}else if (!strpos($v,':')) {
								$my_row[] = $this->add_column_to_rowset($column, $this->rs->UserDate($v,$this->dateFormat) );
							}
							break;
						case 'T':
							if (empty($v)) {
								$my_row[] = $this->add_column_to_rowset($column,'&nbsp;');
							}else{
								$my_row[] = $this->add_column_to_rowset($column,$this->rs->UserTimeStamp($v,$this->dateFormat));
							}
						break;
						
						case 'N':
							if (abs(abs($v) - round($v,0)) < 0.00000001){
								$v = round($v);
							}else{
								$v = round($v,$ADODB_ROUND);
							}
						case 'I':
							$my_row[] = $this->add_column_to_rowset($column,stripslashes((trim($v) ) ));
						   	
						break;
	
						default:
							if ( $this->htmlSpecialChars ) $v = htmlspecialchars(trim($v));
							$v = trim($v);
							if (strlen($v) == 0)
								$v = '&nbsp;';
							$my_row[] = $this->add_column_to_rowset($column,str_replace("\n",'<br />',stripslashes($v)));
						}
					} // end if userfunc
			} // for
			$selected_row_item = false;
			if( (!$_GET[ $this->get_row_selector ] and !$selected_row ) or ($_GET[ $this->get_row_selector ] == $index_id)){
				$selected_row = true;
				$selected_row_item = true;
				$this->selected_row = $index_id;
			}
			$content_rows[] = array('row' => $my_row,
				'link' => xtc_href_link($this->page_file,xtc_get_all_get_params(array($this->get_row_selector)).$this->get_row_selector.'='.$index_id),
				'selected'=>$selected_row_item,
				'id'=>$index_id
			);
			$rows += 1;
			if ($rows >= $gSQLMaxRows) {
				$rows = "<p>Truncated at $gSQLMaxRows</p>";
				break;
			} // switch

			$this->rs->MoveNext();
		
			// additional EOF check to prevent a widow header
			if (!$this->rs->EOF && $rows % $gSQLBlockRows == 0) {
echo $rows . ' ' .$gSQLBlockRows;		
				//if (connection_aborted()) break;// not needed as PHP aborts script, unlike ASP
				die('YES4CARRIER: POSSIBLE WIDOW HEADER IN class.adodb-pager.php');
				if ($echo) print $s . "</TABLE>\n\n";
				else $html .= $s ."</TABLE>\n\n";
				$s = $hdr;
			}
		} // while
		$smarty_array['content_rows'] = $content_rows;
		$smarty_array['num_rows'] = $rows;
		$smarty_array['active_row'] = $this->selected_row;
		$smarty_array['AJAX_PAGER_VALUE'] = $this->page_file;
		if(xtc_not_null($this->pager_tab_file)){
			$smarty_array['AJAX_BOX_ACTIVE'] = true;
		}
		if($this->javascript_row_click_action){
			$smarty_array['ROW_CLICK_ACTION'] = true;
		}
		return $smarty_array;
	}
	
	/**
	 * draw select all/none links for un/checking listitems checkboxes
	 *
	 * @return string
	 */
	function draw_list_selector(){
		$string = '<span class="main">'.TEXT_CHOICE.':';
		$string .= ' <a class="main" href="javascript:checkbox_toggle(\'pagergrid_form\',\'id[]\',true);">'.TEXT_CHOICE_ALL.'</a>&nbsp;<a class="main" href="javascript:checkbox_toggle(\'pagergrid_form\',\'id[]\',false);">'.TEXT_CHOICE_NONE.'</a></span><br />';
		return $string;
	}
	
	/**
	 * hide columns from output
	 *
	 * @param array $columns
	 */
	function hide_columns($columns){
		foreach($columns as $k=>$col){
			$this->hide_cols[] = $col;
		}
	}
	
	/**
	 * if grid in a ajax div the links are prepared for in-div-paging
	 *
	 * @param string $div_id
	 */
	function set_in_div($div_id){
		$this->in_div_links = true;
		$this->in_div_div_id = $div_id;
	}
	
	function make_link($link_text,$get_params){
		$css_class = ($this->linkCssClass != '')?' class="'.$this->linkCssClass.'" ':'';
		$href = xtc_href_link($this->page_file,$get_params);
		$href = ($this->in_div_links) ? "javascript:ajax_snippet_href('".($this->in_div_div_id)."','".$get_params."');" : $href;
		$link = sprintf("<a %s href=\"%s\">%s</a> &nbsp;",$css_class,$href,$link_text); 
		return $link;
	}

	function set_additional_get_params($value_array){
		$this->additional_get_params = $value_array;
	}
	
	function get_additional_get_params(){
		return $this->additional_get_params;
	}
}

// specific code for tohtml
global $gSQLMaxRows,$gSQLBlockRows,$ADODB_ROUND;

$ADODB_ROUND=4; // rounding
$gSQLMaxRows = 1000; // max no of rows to download
$gSQLBlockRows=MAX_DISPLAY_SEARCH_RESULTS_ADMIN; // max no of rows per table block
