PHP Classes

File: as_reportool.php

Recommend this page to a friend!
  Classes of Alexander Selifonov   Report generating and drawing   as_reportool.php   Download  
File: as_reportool.php
Role: Class source
Content type: text/plain
Description: main class module
Class: Report generating and drawing
Generate reports from database query results
Author: By
Last change: new features added and documented
Date: 11 years ago
Size: 40,340 bytes
 

Contents

Class file image Download
<?php /** * @package as_reportool - class for Html Report composing & printing * @name as_reportool.php * @author Alexander Selifonov <as-works@narod.ru> * @link http://www.selifan.ru * @license http://www.gnu.org/copyleft/gpl.html * Updated 2013-09-16 * @version 1.23.023 **/ define('ASREPT_ERR_WRONGXMLFILE', -101); define('ASREPT_ERR_WRONGFORMAT', -102); define('ASREPT_ERR_WRONGPHP', -201); /** * @desc CReportField - one reporting field definition */ $_lbpath = defined('LIBPATH')? LIBPATH: ''; @require_once($_lbpath.'as_dbutils.php'); # db access wrapper class /** * report parameter definition class */ class CReportParam { var $id; var $inputtype; var $type; var $prompt; var $fillfunc; var $mandatory; var $expression = ''; private $linkedCells = array(); private $groupLinks = array(); function CReportParam($id,$itype='text', $type='char', $prompt='',$ffunc='',$expr='',$mand=0) { $this->id=$id; $this->inputtype=$itype; $this->type=$type; $this->prompt = empty($prompt) ? $id : $prompt; $this->fillfunc=$ffunc; $this->expression = $expr; $this->mandatory = $mand; } } class CReportField { var $fieldno; var $title; # title for this field var $formid = ''; var $summable; var $fconv; # '' or function name to make presentation string from value var $format; # r=right, c=center, money= "money fmt",... var $calcfunc = false; # any field can be "calculated" - here is the user callback function name to compute value function CReportField($title='',$summable=0, $fconv='', $format='', $calcfunc=false) { $this->title=$title; $this->summable=$summable; $this->fconv=$fconv; $this->format=$format; $this->calcfunc=$calcfunc; } } class CReporTool { // main class def. const ASREPT_ERR_WRONGXMLFILE = -101; const ASREPT_ERR_WRONGFORMAT = -102; const ASREPT_ERR_WRONGPHP = -201; const CRLF = "\r\n"; var $_title = ''; var $_query = array(); # main query, that gets all info for report var $_srcfile = ''; var $_headers = ''; # header rows for the top header var $_rfields = array(); # fields to draw or calculate var $_grpfields = array(); var $_sumfields = array(); var $_totals = array(); # internal use - accumulated totals var $_summary = array(); private $_totdf = false; # if TRUE, plain tab-delimited body will be created (for importing into spreadsheets like MS Excel private $_xlsFmt = false; # if TRUE, decimal point will convert to "," private $extcss = array(); private $_groupedHeadings = array(); # describes grouping of some heading columns private $_enumGroups = false; # print group's Number in the group headers (since: 1.23 var $_multitotals = ''; private $_grp_curval = array(); private $_grpNo = array(); var $_supress_eg = true; # supress printing headers and subtotals for zeroed grouping field values var $_debug = 0; # set to positive integer N to break reporting after N-th line var $_summarytitle = ''; # if not empty, summary totals line will be printed at report's bottom var $_summaryQuery = ''; # Query to calculate total summary, instead of simple summing row data var $_fontstyles = ''; var $_rowcount = 0; var $errorcode = 0; var $_errormessage = 0; var $_delim_tho = ','; # delimiters for number_format var $_delim_dec = '.'; var $_outcharset = ''; var $_incharset = ''; var $_nodefhead = 0; var $_suppresscss = false; var $_muids = array(); # in multi-totals case here will be saved all total "key" values var $_rownumbers = false; # print row ¹ at first cell var $bg_totals = '#fff'; var $bg_headings = '#eee'; var $bg_newgroup = '#eaeaea'; var $bordercolor = '#111'; static $_backenduri = ''; var $_rowbg_callback = ''; # User callback function to dynamically create data row background color var $_params = array(); # CReportParam object array private $_tofilelink = array(); # 'title'-> linktitle, 'url' => address to get "exported" text version of report private $_outbuf = ''; private $_outname = ''; private $rdata = array(); # raw data to draw in report (user can pass ready-to-print assoc.array by setReportData()) /** * constructor * * @param string $filename passed XML file name to load report definition from * @return CReporTool */ function CReporTool($par=false, $cset=null) { self::$_backenduri = $_SERVER['PHP_SELF']; if(is_array($par)) { if(isset($par['filename'])) $this->_srcfile = trim($par['filename']); if(isset($par['outcharset'])) $this->_outcharset = trim($par['outcharset']); if(isset($par['backend'])) self::$_backenduri = trim($par['backend']); if(isset($par['bg_totals'])) $this->bg_totals = trim($par['bg_totals']); if(isset($par['bg_headings'])) $this->bg_headings = trim($par['bg_headings']); if(isset($par['bg_newgroup'])) $this->bg_newgroup = trim($par['bg_newgroup']); if(isset($par['bordercolor'])) $this->bordercolor = trim($par['bordercolor']); if(isset($par['func_rowbg'])) $this->_rowbg_callback = trim($par['func_rowbg']); if(isset($par['tofilelink'])) $this->_tofilelink = $par['tofilelink']; if(isset($par['decimalPoint'])) $this->_delim_dec = (string)$par['decimalPoint']; if(!empty($par['xlsFmt'])) $this->_xlsFmt = true; if(!empty($par['totdf'])) $this->_totdf = true; if(!empty($par['groupnumbers'])) $this->_enumGroups = true; elseif(!empty($_GET['_tofile']) || !empty($_POST['_tofile'])) $this->_totdf = true; $this->_outname = (!empty($par['outname']))? trim($par['outname']) : 'created-report.txt'; $this->_rownumbers = isset($par['rownumbers']) ? !empty($par['rownumbers']) : false; } elseif(is_scalar($par)) { $this->_srcfile = $par; $this->_outcharset = $cset; } if(is_string($this->_srcfile) && file_exists($this->_srcfile) && function_exists('simplexml_load_file')) { $this->LoadFromXml($this->_srcfile); } } private function _InGroupHead($grp, $fid) { $fcount =1; } function SetBackendURI($parm) { self::$_backenduri = $parm; } /** * Adds field list that must be grouped in the heading with desired "group" title * @since 1.20 * @param mixed $groupTitle * @param mixed $fieldlist */ function AddHeadingGroup($groupTitle, $field, $fcount=2) { if($fcount>1) { $this->_groupedHeadings[] = array($groupTitle, $field, intval($fcount)); } } function LoadFromXml($filename, $outcharset=null) { # parse xml file and load all report parameters ini_set('zend.ze1_compatibility_mode', 0); if(!function_exists('simplexml_load_file')) { $this->_errorcode = self::ASREPT_ERR_WRONGPHP; return false; } $xml = @simplexml_load_file($filename); $this->_outcharset = ($outcharset!==null) ? $outcharset : 'UTF-8'; if(!$xml) { # echo '<pre>'.htmlspecialchars(file_get_contents($filename)).'</pre>'; $this->_errorcode = self::ASREPT_ERR_WRONGXMLFILE; $this->_errormessage = "$filename has wrong XML format or non XML at all!"; echo "Error {$this->_errorcode} - {$this->_errormessage}<br />"; return false; } $this->_errorcode=0; $this->_errormessage=''; $this->_query = array(); $goodfmt = false; if(isset($xml->headings)) { $header = $this->DecodeCharValue($xml->headings); $this->SetHeadings($header); } foreach($xml->children() as $cid=>$obj) { switch($cid) { case 'title': $this->_title = $this->DecodeCharValue($obj); break; case 'parameter': $id = isset($obj['id'])? trim($obj['id']) : ''; $itype = isset($obj['inputtype'])? trim($obj['inputtype']) : 'text'; $type = isset($obj['type'])? trim($obj['type']) : ''; $prompt = isset($obj['prompt'])? $this->DecodeCharValue($obj['prompt']) : $id; $ffunc = isset($obj['fillfunc'])? trim($obj['fillfunc']) : ''; $expression = "$obj"; # tag value is "expression" to put into SQL, "%value%" will be replaced with user entered value if($expression=='') $expression="$id=%value%"; $mand = isset($obj['mandatory'])? trim($obj['mandatory']) : 0; if(!empty($id)) $this->_params[] = new CReportParam($id,$itype,$type,$prompt,$ffunc,$expression,$mand); break; case 'paramform': $id = isset($obj['id'])? trim($obj['id']) : ''; $this->formid = $id; break; case 'query': $this->AddQuery($obj); break; case 'grpfield': $fldid = isset($obj['name'])? trim($obj['name']) : ''; $fconv = isset($obj['fconv'])? trim($obj['fconv']) : ''; $title = isset($obj['title'])? $this->DecodeCharValue($obj['title']) : ''; $ttitle = isset($obj['totaltitle'])? $this->DecodeCharValue($obj['totaltitle']) : ''; if(!empty($fldid)) { $this->AddGroupingField($fldid,$fconv,$title,$ttitle); } break; case 'field': $fldid = isset($obj['name'])? trim($obj['name']) : ''; $title = isset($obj['title'])? $this->DecodeCharValue($obj['title']) : ''; $summa = isset($obj['summable'])? trim($obj['summable']) : ''; $fconv = isset($obj['fconv'])? trim($obj['fconv']) : ''; $fmt = isset($obj['format'])? trim($obj['format']) : ''; if(!empty($fldid)) { $this->AddField($fldid,$title,$summa,$fconv, $fmt); $goodfmt = true; } break; case 'nodefaultheadings': $this->_nodefhead = isset($obj['value'])? trim($obj['value']) : 0; break; case 'headinggroup': # <headinggroup title="Group title" startfield="fieldname" fcount="4" /> $hd_field = isset($obj['startfield'])? trim((string)$obj['startfield']) : ''; $hd_fcount = isset($obj['fcount'])? trim((string)$obj['fcount']) : 0; $hd_title = isset($obj['title'])? trim((string)$obj['title']) : "Group:$hd_field"; if($hd_field!='' && $hd_fcount>1) $this->AddHeadingGroup($hd_title,$hd_field,$hd_fcount); break; case 'fontstyles': $this->_fontstyles = (!empty($obj['value']))? $obj['value'] : ''; break; case 'delimiters': $dec = (!empty($obj['decimal']))? $obj['decimal'] : ''; $tho = (!empty($obj['thousand']))? $obj['thousand'] : ''; if($dec) $this->_delim_dec = $dec; if($tho) $this->_delim_tho = $tho; break; case 'multitotals': $this->_multitotals = isset($obj['byfield'])? trim((string)$obj['byfield']) : ''; break; case 'summary': $title = (!empty($obj['title']))? $this->DecodeCharValue((string)$obj['title']) : 'summary'; $this->SetSummary($title); break; } } if(!$goodfmt) { $this->_errorcode = self::ASREPT_ERR_WRONGFORMAT; $this->_errormessage = "$filename is not AS_REPORTOOL or empty XML file !"; } unset($xml); return true; } function DecodeCharValue($strg) { $ret = $strg; if($this->_outcharset!='' && $this->_outcharset!='UTF-8' && function_exists('mb_convert_encoding')) $ret = @mb_convert_encoding($ret,$this->_outcharset,'UTF-8'); $ret = str_replace(array('&gt;','&lt;'), array('>','<'),$ret); return $ret; } function SetCharSet($par) { $this->_outcharset=strtoupper($par); if(count($this->_rfields)>0 && $this->_outcharset!='' && $this->_incharset!='' && $this->_outcharset!=$this->_incharset) { @mb_convert_variables($this->_outcharset,'UTF-8',$this->_rfields, $this->_grpfields); $this->_summarytitle = mb_convert_encoding($this->_summarytitle,$this->_outcharset,'UTF-8'); } } /** * register one field to draw in report page * * @param mixed $fldid field name in the query * @param mixed $fldtitle title in the headers for the field * @param mixed $summable if not 0, make sub-totals for this field * @param mixed $fconv function name for converting field value to something readable, if needed (name for id etc.) * @param mixed $calcfunc user callback function to calculate field value */ function AddField($fldid,$fldtitle='',$summable=0,$fconv='', $format='', $calcfunc=false) { # if(empty($fldtitle)) $fldtitle=$fldid; $this->_rfields[$fldid] = new CReportField($fldtitle,$summable,$fconv,$format, $calcfunc); if($summable) $this->_sumfields[] = $fldid; } /** * Passing user data (instead of performing SQL queries) * * @param mixed $userdata multi-row array, each row should be an assoc.array with keys according to printed field definitions */ function setReportData($userdata) { $this->rdata = is_array($userdata) ? $userdata : array(); } function SetHeaders($par,$no_defaultheader=0) { $this->_headers = $par; $this->_nodefhead=$no_defaultheader; } function SetHeadings($par,$no_defaultheader=0) { $this->_headers = $par; $this->_nodefhead=$no_defaultheader; } function SetQuery($sqlquery) { $this->_query=array(); $this->_query[]=$sqlquery; } function AddQuery($sqlquery) { $this->_query[]=$sqlquery; } function SetSummary($par='Totals:', $sumQry=false) { $this->_summarytitle = $par; if(!empty($sumQry)) $this->_summaryQuery = $sumQry; } function SetFontStyles($par='') { $this->_fontstyles = $par; } function AddGroupingField($fldid,$fconv='',$title='',$totaltitle='') { $this->_grpfields[$fldid] = array('fconv'=>$fconv,'title'=>$title,'ttitle'=>$totaltitle); } /** * sets decimal and thousand delimiters for number_format() * * @param mixed $dec * @param mixed $tho */ function SetNumberDelimiters($dec,$tho='') { $this->_delim_dec = $dec; $this->_delim_tho = $tho; } function SetMultiTotals($fieldname) { $this->_multitotals = $fieldname; } /** * Describes "linked" cell, that will become a href * * @param mixed $fieldid - field id * @param mixed $options - associative array with options: <ul> * <li>uri - URI for the href or '@func_name' to evaluate URI dinamically</li> * <li>idparts - comma delimited field list that should be placed in URIinstead of {1},{2} and so on</li> * <li>'target' =>'_blank' to open sub-report in a new window</li> * <li>'title' =>'title for the link' * </ul> * @since 1.12 */ function SetLinkedCell($fieldid, $options) { if(isset($options['uri'])) $this->linkedCells[$fieldid] = $options; } /** * Defines rule for ceratin "<a href" link for "group" headers, to allow "nested" sub-reports * * @param mixed $options must be an assoc/array with elements: 'href'=> URL with "{1},{2}" to be substituted by current value, * 'idparts' for a list of values to substitute 'title'=>optional title string */ function SetGroupLink($fieldid, $options) { if(is_array($options)) $this->groupLinks[$fieldid] = $options; } # adds custom css file to include into report HTML headers function AddCssFile($cssuri) { $this->extcss[] = $cssuri; } /** * Turns off output of builtin css block * * @param mixed $par if non-empty value, css won't be printed */ function SuppressCss($par=true) { $this->_suppresscss = $par; } function getFieldOffset($fldname) { $ret = array_search($fldname, array_keys($this->_rfields)); return $ret; } /** * Rendering report headings, including possible "grouping" of some fields headings * @since 1.20 */ function DrawReportHeadings() { if(!count($this->_groupedHeadings) || ($this->_totdf)) { $thead = $this->_totdf ? '' : '<tr class="rep_heading">'; if($this->_rownumbers) $thead .= $this->_totdf ? "¹\t" : "<td class='rep_ltrb cnt'>¹</td>"; foreach($this->_rfields as $fid=>$fld) { if($fld->title!='') $thead .= $this->_totdf ? (strip_tags($fld->title)."\t") : "<td class=\"rep_ltrb\">{$fld->title}</td>"; } $thead .= $this->_totdf ? self::CRLF : '</tr>'; } else { # There are groupings in header columns, so we draw a "complex" headings $hdrs = array(); foreach($this->_rfields as $fid=>$fld) { $hdrs[$fid] = 0; # how many times field exist in griouping } $hRows = array(); $hRows[] = array(); $fids = array_keys($this->_rfields); foreach($this->_groupedHeadings as $grh) { $fieldId = $grh[1]; $fOffset = $this->getFieldOffset($grh[1]); $cnt = $grh[2]; # $hRows[] = array(); $thisrowNo = 0; for($kk=$fOffset; $kk<$fOffset+$cnt;$kk++) { $thisrowNo = max($thisrowNo,$hdrs[$fids[$kk]]); $hdrs[$fids[$kk]]+=1; } if(!isset($hRows[$thisrowNo])) $hRows[$thisrowNo] = array(); $hRows[$thisrowNo][$fOffset] = array('text'=>$grh[0],'colspan'=>$cnt,'rowspan'=>1); } $maxrowspan = count($hRows); # Fields that not in any grouped heaqder, will have this rowspan $rownumspan = $maxrowspan+1; $thead = ''; for($iRow=0; $iRow<=$maxrowspan; $iRow++) { $rowcontent = ($iRow==0 && $this->_rownumbers) ? "<td class='rep_ltrb cnt' rowspan='$rownumspan'>¹</td>": ''; for($ifld=0; $ifld<count($this->_rfields); $ifld++) { if(isset($hRows[$iRow][$ifld])) { $rspan = $hRows[$iRow][$ifld]['rowspan']>1 ? " rowspan='".$hRows[$iRow][$ifld]['rowspan']."'" : ''; $cspan = $hRows[$iRow][$ifld]['colspan']>1 ? " colspan='".$hRows[$iRow][$ifld]['colspan']."'" : ''; $rowcontent .= "<td class='rep_ltrb'{$rspan}{$cspan}>".$hRows[$iRow][$ifld]['text'].'</td>'; } else { # normal field title $fid = $fids[$ifld]; if($iRow == $hdrs[$fid]) { $rowspan = $maxrowspan - $iRow+1; $rspan = $rowspan>1 ? " rowspan='$rowspan'" : ''; $rowcontent .= "<td class='rep_ltrb'{$rspan}>".$this->_rfields[$fid]->title.'</td>'; } } } if($rowcontent) $thead .= '<tr class="rep_heading">' .$rowcontent. '</tr>'; } } $this->_outbuf .= $thead; } /** * runs SQL query and echoes resulting report * */ function DrawReport($title='', $buffered = false) { global $as_dbengine; $this->_outbuf = ''; $this->_grp_curval = $this->_grpNo = array(); $this->_totals = array(); if(empty($title)) $title = $this->_title; foreach($this->_grpfields as $grpfld=>$grp) { $this->_grp_curval[$grpfld]='{*}'; $this->_grpNo[$grpfld]=0; } foreach($this->_rfields as $fid=>$fld) { # init subtotals values array $this->_totals[$fid] = array(); foreach($this->_sumfields as $sumf) { $this->_totals[$fid][$sumf] = ($this->_multitotals)? array():0; } } foreach($this->_sumfields as $sumf) { $this->_summary[$sumf] = ($this->_multitotals)? array():0; } $fnt = ($this->_fontstyles=='')? '': $this->_fontstyles; if(count($this->extcss)) foreach($this->extcss as $cssuri) { $this->_outbuf .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"$cssuri\" />\n"; } if(!$this->_suppresscss && !$this->_totdf) { $this->_outbuf .= <<< EOCSS <style type="text/css"> /** styles for report drawing **/ table.as_report { border-collapse: collapse; } table.as_report td { border-spacing: 4px; border: 1px solid {$this->bordercolor}; padding: 3px;} td.rep_ltrb { border: 1px solid; text-align:center; $fnt} td.rep_lrb { border-left:1px solid; border-top:none; border-right: 1px solid #000; border-bottom: 1px solid #000; text-align:left; $fnt} td.rep_rb { border-left:none; border-top:none; border-right: 1px solid; border-bottom: 1px solid; text-align:left; $fnt } .rep_totals { background-color:{$this->bg_totals}; $fnt } .rep_heading { background-color: {$this->bg_headings}; $fnt } td.num { text-align: right; } td.cnt { text-align: center; margin-left:auto;margin-right:auto;} td.newgroup { background: {$this->bg_newgroup}; $fnt } </style> EOCSS; } $colsp = count($this->_rfields); if(!$this->_totdf) { $toXlsLink = isset($this->_tofilelink['url']) ? ("<a href='".$this->_tofilelink['url']."' target='_blank'>".$this->_tofilelink['title'].'</a>') : ''; $this->_outbuf .= "<h4 style='text-align:center; font-weight:bold;'>$title $toXlsLink </h4>\n <center><table class='as_report'>{$this->_headers}"; } else $this->_outbuf .= $title . self::CRLF.self::CRLF; # draw report field headers if(empty($this->_nodefhead)) { $this->DrawReportHeadings(); } $iquery = -1; $rlink = count($this->rdata)>0 ? -2 : -1; $rdatarow = 0; if(is_array($this->rdata)) reset($this->rdata); $this->_rowcount = 0; while(true) { if($rlink===-1) { $iquery++; if(!isset($this->_query[$iquery])) break; $rlink = $as_dbengine->sql_query($this->_query[$iquery]); if(!is_resource($rlink)) { $rlink=-1; continue; } # try next query } if($rlink == -2) { $r = current($this->rdata); next($this->rdata); $rdatarow++; } else $r=$as_dbengine->fetch_assoc($rlink); if(!$r) { if($rlink == -2) $rlink = -1; elseif($rlink>0) { $as_dbengine->free_result($rlink); $rlink = -1; } continue; } # process next query in queue foreach($this->_rfields as $fid=>$fdef) { # compute "auto-calculated" fields if(!empty($fdef->calcfunc) && is_callable($fdef->calcfunc)) { $r[$fid] = @call_user_func($fdef->calcfunc,$r); } } # check if some "grouping" field values changed, draw subtotals if so $this->_rowcount++; if($this->_multitotals && !in_array($r[$this->_multitotals],$this->_muids)) $this->_muids[] = $r[$this->_multitotals]; $grp_ffield = ''; # becomes a name of the "major" grouping field that has changed foreach($this->_grp_curval as $fid=>$curval) { if(isset($r[$fid]) && $curval !== $r[$fid]) { if($curval!=='{*}') { # draw and reset all accumulated sub-totals $this->__DrawSubtotals($fid); } $this->_grp_curval[$fid]=$r[$fid]; $grp_ffield=$fid; $b_tmp = false; foreach($this->_grpfields as $grid=>$grp) { if($grid==$grp_ffield) { $b_tmp=true; continue; } if($b_tmp) { $this->_grp_curval[$grid]='{*}'; $this->_grpNo[$grid] = 0; } } if(!$b_tmp) $grp_ffield=''; break; } } if($grp_ffield!=='') { # draw headers for next sub-total group if($this->_debug>0 && $this->_rowcount >= $this->_debug) break; # debug stop $ingrp = false; $leftoff=''; foreach($this->_grpfields as $fid=>$grp) { if($fid==$grp_ffield) $ingrp=true; if($ingrp) { if(($this->_supress_eg) && empty($r[$fid])) continue; # don't print header for empty grouping value $fldval = (!empty($grp['fconv']) && function_exists($grp['fconv']))? call_user_func($grp['fconv'],$r[$fid],$r): $r[$fid]; $txt = $grp['title'].' '.$fldval; if($this->_enumGroups) { ++$this->_grpNo[$fid]; $txt = $this->_grpNo[$fid] . ' : ' . $txt; } $colsp = count($this->_rfields) + ($this->_rownumbers ? 1:0); # make linked group header ? $grpstring = $leftoff . $txt; if(isset($this->groupLinks[$fid])) { # make "a href" linked group header $href = $this->_eval($this->groupLinks[$fid]['uri'], $r); if($href!='') { $idparts = isset($this->groupLinks[$fid]['idparts']) ? explode(',',$this->groupLinks[$fid]['idparts']) : array(); $replarray = array(); for($ii=0;$ii<count($idparts); $ii++) { $i2 = $ii+1; $replarray["{{$i2}}"] = isset($r[$idparts[$ii]]) ? $r[$idparts[$ii]] : $i2; } if(count($replarray)) $href = str_replace(array_keys($replarray), array_values($replarray), $href); $hopts1 = empty($this->groupLinks[$fid]['title']) ? '' : " title='". $this->_eval($this->groupLinks[$fid]['title'],$r)."'"; $hopts2 = empty($this->groupLinks[$fid]['target']) ? '' : " target='".$this->groupLinks[$fid]['target']."'"; $grpstring = "<a href=\"$href\"{$hopts1}{$hopts2}>$grpstring</a>"; } } # make linked group header ? if($this->_totdf) $this->_outbuf .= $leftoff . $txt . self::CRLF; else $this->_outbuf .= "<tr><td class=\"rep_lrb newgroup\" colspan=\"$colsp\">{$grpstring}</td></tr>\n"; $this->_grp_curval[$fid]=$r[$fid]; } $leftoff.=' &nbsp;&nbsp;'; } } # now draw "normal" report row $color = ($this->_rowbg_callback) && function_exists($this->_rowbg_callback) ? call_user_func($this->_rowbg_callback, $r) : ''; $trattr = $tdattr = $class = $styles = ''; if(!empty($r['_row_class_'])) $class = $r['_row_class_']; if(($color) && empty($this->_totdf)) { if(is_string($color)) $trattr = " style='background-color:$color'"; elseif(is_array($color)) { $attr = array( 'style' => (isset($color['style']) ? $color['style'] : '') ,'background-color' => (isset($color['background-color']) ? $color['background-color'] : '') ); if(isset($color['class'])) $class = $color['class']; # css class can be passed as '_row_class_' element in data array: foreach($attr as $atkey=>$atval) { if($atval) $styles.= "$atkey:$atval;"; } if(!empty($color['font'])) $tdattr = " style='font:$color[font]'"; } } if($class) $trattr .= " class='$class'"; if($styles) $trattr = " style='$styles'"; $strow = $this->_totdf ? '': "<tr{$trattr}>"; $colno = 0; if($this->_rownumbers) $strow .= $this->_totdf ? ($this->_rowcount . "\t") : "<td class='rep_lrb cnt'$tdattr>{$this->_rowcount}</td>"; foreach($this->_rfields as $fid=>$fld) { $cls = ((++$colno>1) or $this->_rownumbers) ? 'rep_rb':'rep_lrb'; # Needed only for MSIE 6 that doesn't know css collapse ? if(in_array($fld->format, array('c','d','%'))) $cls .= ' cnt'; elseif(in_array($fld->format, array('r','right','money','i','d2'))) $cls .= ' num'; if(isset($r[$fid])) { if (!empty($fld->fconv) && function_exists($fld->fconv)) $value=call_user_func($fld->fconv,$r[$fid],$r); else $value = $this->FormatValue($fid,$r[$fid]); if(in_array($fid,$this->_sumfields)) { if($this->_multitotals) { $muid=$this->_multitotals; if(isset($this->_summary[$fid][$r[$muid]])) $this->_summary[$fid][$r[$muid]]+=floatval($r[$fid]); else $this->_summary[$fid][$r[$muid]]=floatval($r[$fid]); } else $this->_summary[$fid] +=floatval($r[$fid]); # if(!isset($this->_totals[$fid])) $this->_totals[$fid] = 0; foreach($this->_grpfields as $grpid=>$gr) { if(empty($this->_multitotals)) { if(!isset($this->_totals[$grpid][$fid])) $this->_totals[$grpid][$fid]=0; $this->_totals[$grpid][$fid] += floatval($r[$fid]); } else { #<5> $mulid = $r[$this->_multitotals]; if(!isset($this->_totals[$grpid][$fid][$mulid])) $this->_totals[$grpid][$fid][$mulid]=floatval($r[$fid]); else $this->_totals[$grpid][$fid][$mulid] += floatval($r[$fid]); } #<5> } # $cls .= ' num'; } } else { $value = @function_exists($fld->fconv)? call_user_func($fld->fconv,0,$r) : ($this->_debug ? "($fid)" : ' '); } if($value==='') $value='&nbsp;'; if(isset($this->linkedCells[$fid])) { # make linked cell $href = $this->_eval($this->linkedCells[$fid]['uri'],$r); if($href!='') { $idparts = isset($this->linkedCells[$fid]['idparts']) ? explode(',',$this->linkedCells[$fid]['idparts']) : array(); $replarray = array(); for($ii=0;$ii<count($idparts); $ii++) { $i2 = $ii+1; $replarray["{{$i2}}"] = isset($r[$idparts[$ii]]) ? $r[$idparts[$ii]] : $i2; } if(count($replarray)) $href = str_replace(array_keys($replarray), array_values($replarray), $href); $ttl = empty($this->linkedCells[$fid]['title']) ? '' : " title='".$this->_eval($this->linkedCells[$fid]['title'],$r)."'"; $target = empty($this->linkedCells[$fid]['target']) ? '' : " target='".$this->linkedCells[$fid]['target']."'"; $value = "<a href=\"$href\"{$ttl}{$target}>$value</a>"; } } $strow .= ($this->_totdf)? "$value\t" : "<td class=\"$cls\"{$tdattr}>$value</td>"; } $strow .= ($this->_totdf) ? self::CRLF : "</tr>\n"; $this->_outbuf .= $strow; } if(is_resource($rlink)) $as_dbengine->free_result($rlink); if(count($this->_grpfields)) $this->__DrawSubtotals(); if(!empty($this->_summarytitle)) $this->__DrawSummaryTotals(); if(!$this->_totdf) $this->_outbuf .= "</table></center>"; if($this->_totdf) { $this->_outbuf = strip_tags($this->_outbuf); # delete all generated <a href...> $this->_outbuf = str_replace('&nbsp;',' ',$this->_outbuf); if(!headers_sent()) { Header('Content-Type: text/plain'); Header("Content-Disposition: attachment; filename=\"$this->_outname\""); Header('Content-Length: '.strlen($this->_outbuf)); } exit($this->_outbuf); } elseif($buffered) return $this->_outbuf; else echo $this->_outbuf; } /** * Evaluates possible '@function_name' by calling user function * * @param mixed $param * @param mixed $args */ private function _eval($param,$args=false) { $ret = $param; if(substr($param,0,1)=='@') { $fncname = substr($param,1); if(function_exists($fncname)) $ret = call_user_func($fncname, $args); } return $ret; } /** * internal function, draws all sub-totals from lowest level to $gfield level * * @param string $gfield upper level grouping field */ function __DrawSubtotals($gfield='') { if($this->_totdf) return; $grfields = array_reverse($this->_grpfields); # go up from "inner" subtotal level $b_draw= true; foreach($grfields as $fid=>$grp) { # .. draw sub-total row; if(($this->_supress_eg) && empty($this->_grp_curval[$fid])) continue; # don't print subtotals if empty grouping value $ttval = $this->FormatValue($fid,$this->_grp_curval[$fid], $grp['fconv']); $ttpl = empty($grp['ttitle']) ? 'Total for %name%': $grp['ttitle']; $totitle = str_replace('%name%',$ttval,$grp['ttitle']); $cspan=0; foreach($this->_rfields as $fldid=>$fld) { if($fld->summable) break; $cspan++; } if($this->_rownumbers) $cspan++; if($cspan<1) $txt = "<tr class='rep_totals'><td class=\"rep_lrb\" colspan=10 >$totitle</td></tr><tr>"; else $txt = "<tr class='rep_totals'><td class=\"rep_lrb\" colspan=\"$cspan\">$totitle</td>"; $b_strt = false; foreach($this->_rfields as $flddid=>$fld) { if(!$fld->summable && !$b_strt) continue; if($fld->summable) { $b_strt = true; if($this->_multitotals) { $value=''; foreach($this->_muids as $muid) $value .= ($value==''? '':'<br />'). $this->FormatValue($flddid,(isset($this->_totals[$fid][$flddid][$muid])?$this->_totals[$fid][$flddid][$muid]:0)); } else $value = isset($this->_totals[$fid][$flddid]) ? $this->FormatValue($flddid,$this->_totals[$fid][$flddid]) : ''; $txt .= "<td class=\"rep_rb num\" nowrap=\"nowrap\">$value</td>"; if($this->_multitotals) { foreach($this->_muids as $muid) $this->_totals[$fid][$flddid][$muid]=0; } else $this->_totals[$fid][$flddid] = 0; } elseif($flddid===$this->_multitotals) { $value = ''; foreach($this->_muids as $muid) $value.=($value===''?'':'<br />').$muid; $txt.= "<td class=\"rep_rb\">$value</td>"; } else $txt .= '<td class="rep_rb num">&nbsp;</td>'; } $txt .="</tr>\n"; $this->_outbuf .= $txt; if(!empty($gfield) && $fid==$gfield) break; # upper level of subtotal reached } } function __DrawSummaryTotals() { global $as_dbengine; if($this->_totdf) return; if($this->_summaryQuery) { # recalculate grand-totals by query executing $this->_summary = $as_dbengine->sql_query($this->_summaryQuery,true,1,0); } $cspan=0; $title = str_replace('%rowcount%',$this->_rowcount,$this->_summarytitle); foreach($this->_rfields as $fldid=>$fld) { if($fld->summable) break; $cspan++; } $colcnt = count($this->_rfields); if($this->_rownumbers) { $colcnt++; $cspan++; } if($cspan<1) $txt = "<tr class='rep_totals'><td class=\"rep_lrb\" colspan=\"$colcnt\" >$title</td></tr><tr>"; else $txt = "<tr class='rep_totals'><td class=\"rep_lrb\" colspan=\"$cspan\">$title</td>"; $b_strt = false; foreach($this->_rfields as $flddid=>$fld) { if(!$fld->summable && !$b_strt) continue; if($fld->summable) { $b_strt = true; if($this->_multitotals) { $value = ''; foreach($this->_muids as $muid) $value .= ($value==''? '':'<br />'). $this->FormatValue($flddid,(isset($this->_summary[$flddid][$muid])?$this->_summary[$flddid][$muid]:0)); } else $value = $this->FormatValue($flddid,$this->_summary[$flddid]); $txt .= "<td class=\"rep_rb num\" nowrap=\"nowrap\">$value</td>"; } elseif($flddid===$this->_multitotals) { $value = ''; foreach($this->_muids as $muid) $value.=($value===''?'':'<br />').$muid; $txt.= "<td class=\"rep_rb cnt\">$value</td>"; } else $txt .= '<td class="rep_rb cnt">&nbsp;</td>'; } $txt .="</tr>\n"; $this->_outbuf .= $txt; } function HasParameters(){ return (count($this->_params)>0); } function FormatValue($fieldid, $value) { $conv = !empty($this->_rfields[$fieldid]->fconv) ? $this->_rfields[$fieldid]->fconv : (!empty($this->_grpfields[$fieldid]['fconv']) ? $this->_grpfields[$fieldid]['fconv']:''); $fmt = !empty($this->_rfields[$fieldid]->format) ? $this->_rfields[$fieldid]->format : ''; if(!empty($conv) && function_exists($conv)) $ret = call_user_func($conv,$value); elseif($fmt==='money' or $fmt==='d2') { if($this->_xlsFmt) $ret = number_format($value,2,$this->_delim_dec, ''); elseif(!$this->_totdf) $ret = number_format($value,2,$this->_delim_dec, $this->_delim_tho); else $ret = floatval($value); } elseif($fmt==='i') { if($this->_xlsFmt) $ret = number_format($value,0,$this->_delim_dec, ''); elseif(empty($this->_totdf)) $ret = number_format($value,0,$this->_delim_dec, $this->_delim_tho); else $ret = floatval($value); } elseif($fmt==='%') { # convert to percents, with decimal with 2 digits after dec.char if($value!=0) { if($this->_xlsFmt) $ret = number_format($value*100,2,$this->_delim_dec, ''); elseif(empty($this->_totdf)) $ret = number_format($value*100,2,$this->_delim_dec, $this->_delim_tho); else $ret = floatval($value*100); } else $ret = $this->_totdf ? 0 : '&nbsp;'; } else $ret = $value; return $ret; } static function DrawJsCode($nostarttag=false, $to_var=false) { global $as_iface; $err_fill = isset($as_iface['err_emptymandatoryparam']) ? $as_iface['err_emptymandatoryparam']:'Not all mandatory parameters set'; $bkuri = self::_backenduri; $jscode = $nostarttag ? '': "<script type=\"text/javascript\">\n"; $jscode .= <<<ENDCRIPT var asrep_activefrm = ''; var reptbkend = "$bkuri"; function asReportShowForm(formid) { if(asrep_activefrm!='') $("#"+asrep_activefrm).hide(); asrep_activefrm = formid; if(formid!="") $("#"+asrep_activefrm).show(); return false; } function asOpenReParamForm(divid) { if(jQuery("#"+divid).get(0)) { jQuery("#"+divid).show(); return; } jQuery("body").floatWindow( { url: reptbkend + "?showform="+divid ,userData: { showform:divid } // ,closeobj: "btncancel" ,title: "Enter parameters!" }); } function asReportExec(formid) { var bfill=true; var pars = ""; alert(formid); // $("#"+formid+" input").each(function() { // var vl = $(this).val(); // alert(this.name+" "+vl); // if(vl=="") bfill=false; // if(this.name) pars += '&'+this.name+"="+encodeURIComponent(vl); // }); // if(!bfill) { alert("<?=$err_fill?>"); return false; } // var wnd=window.open('{self::_backenduri}?reportdef='+name+pars, "_report","location=1,menubar=1,resizable=1,scrollbars=1,status=0,toolbar=1,top=40,left=40"); // wnd.focus(); } ENDCRIPT; if(!$nostarttag) $jscode .= "</script>\n"; if($to_var) return $jscode; echo $jscode; } /** * returns HTML code with <form...> containing all parameters for input * * @param string $formid desired form's name and id */ function DrawParametersForm($formid='') { global $as_iface; $frmid = ($formid)? $formid : $this->formid; if(count($this->_params)<1) return ''; $ret = "<div id=\"div_$frmid\"><form name=\"$frmid\" id=\"$frmid\"><table border=0 cellspacing=1 cellpadding=2>"; foreach($this->_params as $no=>$prm) { $prompt = $prm->prompt; $itag = ''; switch($prm->inputtype) { case 'text': $width = ($prm->type=='char')? '180':'70'; $itag = "<input type=\"text\" name=\"{$prm->id}\" id=\"{$prm->id}\" style='width:{$width}px' class='ibox'/>"; break; case 'select': $opts = @call_user_func($prm->fillfunc); $itag = "<select name=\"{$prm->id}\" >".DrawSelectOptions($opts,0,1).'</select>'; break; case 'const': $val = ($prm->fillfunc) ? @call_user_func($prm->fillfunc) : 0; $ret.= "<input type=\"hidden\" name=\"{$prm->id}\" id=\"{$prm->id}\" value=\"$val\" />"; break; } if($prm->inputtype!=='const') $ret .= "<tr><td>$prompt</td><td>$itag</td></tr>"; } $submtxt = isset($as_iface['prompt_submit'])? $as_iface['prompt_submit']: 'submit'; $ret .="<tr><td>&nbsp;</td><td><input type=\"button\" name=\"submit\" class=\"button\" value=\"$submtxt\" onclick=\"asReportExec('$formid')\"/></td></tr></table></form></div>"; return $ret; } } # CReporTool end