<?php
/**
* DESCRIPTION:
* The ReportList class is designed for easy and extremely functional output of array
* data to HTML and other formats of lists.
*
* AUTHOR(S):
* David Clark (www.theCalico.com)
*
* IMPORTANT NOTE:
* There is no warranty, implied or otherwise with this software.
*
* LICENSE:
* This code has been placed in the Public Domain for all to enjoy.
*
* VERSION FEATURE NOTES:
* 06-08-2007
* -Added support for columns that are hidden by default, with javascript controls
* to show/hide them as the user chooses.
*
* 05-31-2007
* -Minor tweak to add a "border=0" attribute to image-formatted fields.
*
* 05-30-2007
* -Modified XML handling slightly to use field titles instead of field names for
* XML element names (e.g. "<Product_Name>" instead of "<strProductName>", and to
* not include the entire "<ListDescription>" section if there is no title/subtitle
* or other heading information to display.
*
* 05-10-2007
* -Modified behavior for column sorting so that clicking on a column heading once
* sorts in descending order, and then after that switches to ascending (rather than
* ascending first, then descending second as it had been previously)
*
* 04-27-2007
* -Added support for the "image" format (and re-synced the changes from 12/30/2006 that
* had been overwitten in early January)
*
* 12-30-2006
* -Added support for conditional linking (i.e. drill-down links that are dependent
* upon the evaluation of certain test criteria for each record to be displayed.
* -Added support for formula columns in a similar manner.
*
* 10-01-2006
* -Added support for "longint" format (comma-delimited integers, for numeric
* values - does not call intval() on values to allow string representations, e.g.
* None, Infinity, NAN, etc. to be passed through).
*
* 7-27-2006
* -Modified support for sorting slightly to do case-insensitive sorts on strings.
*
* 7-23-2006
* -Added support for column-level link URLs (entirely different base URLs/parameters
*
* 7-15-2006
* -Added support for overall & paged summary rows (e.g. total calculations)
* -Added support for custom column-level attributes (e.g. CSS styles)
*
* 6-22-2006
* -Added int, integer format types
* -Modified html behavior slightly so that the check for an empty field value uses
* -strict equality (===) instead of just regular equality (==) to allow zero values, etc.
* -to be displayed in output (rather than always converting those to values).
*
* 11-3-2004
* -Added bool, yesno format types
*
* 9-18-2004
* -Added support for bottom pagination controls
* -Added support for indicating field truncation when a max column length is set
* -Added support for shortdatetime (mm/dd/yy hh:ii instead of mm/dd/yyyy hh:ii) data format
*
* 8-28-2004
* -Added support for qcsv (Quoted-CSV) output format
* -Added support for shortdate (mm/dd/yy instead of mm/dd/yyyy) data format
* -Corrected support for the decimal format
* -Added support for a maximum length of data values when adding output columns ($intMaxLength parameter)
*
* No crosstab support, multi-line list support untested.
*
* @version 1.0.20070427
* @access public
*/
class ReportList
{
var $_arrTitleInfo;
var $_arrRowInfo;
var $_arrLinkInfo;
var $_arrSortInfo;
var $_arrPagingInfo;
var $_arrColumnInfo;
var $_arrSummaryInfo;
var $_arrCrossTabInfo;
var $_arrGridLayoutInfo;
var $_blnRowIsOdd;
var $_blnDoNoBreaks;
var $_strReportId;
var $_intControlId;
/* --- Public Methods --- */
/**
* Constructor method for the ReportList class.
* @param string $strMainAttributes Any global attributes (table attributes, styles, class, etc.) for the overall
* Html table that will comprise this list.
* @return void
* @access public
*/
function ReportList($strMainAttributes = 'cellspacing="0" cellpadding="3" border="1"', $strEvenRowAttributes = '', $strOddRowAttributes = '', $strHoverRowAttributes = '')
{
$this->_arrTitleInfo = array('strMainAttributes'=>$strMainAttributes, 'strListTitle'=>'', 'strTitleOpenAttributes'=>'', 'strTitleCloseAttributes'=>'', 'strSubTitle'=>'', 'strSubTitleOpenAttributes'=>'', 'strSubTitleCloseAttributes'=>'', 'blnShowRecordCount'=>true, 'strCaptionAttributes'=>'', 'strPageNavigatorAttributes'=>'', 'strFieldHeadingAttributes'=>'');
$this->_arrRowInfo = array('strEvenRowAttributes'=>$strEvenRowAttributes, 'strOddRowAttributes'=>$strOddRowAttributes, 'strHoverRowAttributes'=>$strHoverRowAttributes);
$this->_arrLinkInfo = array('blnAllowLink'=>false, 'strLinkURL' => '', 'strLinkAttributes'=>'', 'arrLinkParamFields'=>array());
$this->_arrSortInfo = array('blnAllowSort'=>false, 'blnSortDescending'=>false, 'strSortField'=>'', 'strSortURL'=>'', 'blnUsePathParams'=>0, 'strSortAnchor'=>'');
$this->_arrPagingInfo = array('blnAllowPaging'=>false, 'strPagingURL'=>'', 'intStartRecord'=>0, 'intNumRecords'=>0, 'strPagingAnchor'=>'');
$this->_arrColumnInfo = array('blnShowColumnNames'=>true, 'blnUseAllColumns'=>true, 'blnIndicateTruncation'=>false, 'arrColumnsToUse'=>array(), 'arrSectionKeys'=>array(), 'strHiddenColumnClassName'=>'');
$this->_arrSummaryInfo = array('blnShowOverallSummary'=>false, 'blnShowPageLevelSummary'=>false, 'arrSummaryFields'=>array(), 'strOverallSummaryAttributes'=>'', 'strPageLevelSummaryAttributes'=>'');
$this->_arrCrossTabInfo = array('blnShowSummaryCol'=>true, 'blnShowSummaryRow'=>true);
$this->_arrGridLayoutInfo = array('blnDoMultiLineList'=>false, 'blnShowFieldNames'=>true, 'strFieldAttributes'=>'', 'intRecordsPerRow'=>1);
$this->_strReportId = '100';
return;
}
/**
* Set the main attributes (table attributes, style, class settings, etc.) for the overall Html table
* that will be used to build this report list.
* @param string $strMainAttributes Any global attributes (table attributes, styles, class, etc.) for the overall
* Html table that will comprise this list.
* @return void
* @access public
*/
function setMainAttributes($strMainAttributes)
{
$this->_arrTitleInfo['strMainAttributes'] = $strMainAttributes;
return;
}
/**
* Set the primary title (and its attributes) to be used for this report.
* @param string $strTitle The title to use for the report
* @param string $strTitleOpenAttributes Any additional attributes to use for the title, e.g. style/class settings, etc.
* @param string $strTitleCloseAttributes Any closing tags necessary to complete the $strTitleOpenAttributes
* @return void
* @access public
*/
function setTitle($strTitle, $strTitleOpenAttributes = '', $strTitleCloseAttributes = '')
{
$this->_arrTitleInfo['strListTitle'] = $strTitle;
$this->_arrTitleInfo['strTitleOpenAttributes'] = $strTitleOpenAttributes;
$this->_arrTitleInfo['strTitleCloseAttributes'] = $strTitleCloseAttributes;
return;
}
/**
* Set the sub-title (and its attributes) to be used for this report. This is useful for adding disclaimers to reports.
* @param string $strSubTitle The sub-title to use for this report
* @param string $strSubTitleOpenAttributes Any additional attributes to use for the sub-title, e.g. style/class settings, etc.
* @param string $strSubTitleCloseAttributes Any closing tags necessary to complete the $strSubTitleOpenAttributes
* @return void
* @access public
*/
function setSubTitle($strSubTitle, $strSubTitleOpenAttributes = '', $strSubTitleCloseAttributes = '')
{
$this->_arrTitleInfo['strListSubTitle'] = $strSubTitle;
$this->_arrTitleInfo['strSubTitleOpenAttributes'] = $strSubTitleOpenAttributes;
$this->_arrTitleInfo['strSubTitleCloseAttributes'] = $strSubTitleCloseAttributes;
return;
}
/**
* Set whether or not to show the report record count.
* @param boolean $blnDoShow True/False for whether or not to show the record count
* @return void
* @access public
*/
function showRecordCount($blnDoShow)
{
$this->_arrTitleInfo['blnShowRecordCount'] = $blnDoShow;
return;
}
/**
* Sets additional attributes (style/class tags, etc.) for the caption (record count) of the report
* @param string $strAttributes Any additional attributes to use for displaying/formatting the report caption
* @return void
* @access public
*/
function setCaptionAttributes($strAttributes)
{
$this->_arrTitleInfo['strCaptionAttributes'] = $strAttributes;
//$this->_strCaptionAttributes = $strAttributes;
return;
}
/**
* Sets additional attributes (style/class tags, etc.) for the page navigation links of the report
* @param string $strAttributes Any additional attributes to use for displaying/formatting the page navigators
* @return void
* @access public
*/
function setPageNavigatorAttributes($strAttributes)
{
$this->_arrTitleInfo['strPageNavigatorAttributes'] = $strAttributes;
return;
}
/**
* Customize the appearance of rows. This allows you to specify different attributes (appearances)
* for odd vs. even rows, and some style attributes to apply on mouse overs.
* @param string $strOddRowAttributes The styles/attributes to apply to odd rows
* @param string $strEvenRowAttributes The styles/attributes to apply to even rows
* @param string $strHoverRowClassName A style class name to apply to active rows on the mouseover event
* @return void
* @access public
*/
function setRowAttributes($strOddRowAttributes, $strEvenRowAttributes, $strHoverRowClassName)
{
$this->_arrRowInfo['strOddRowAttributes'] = $strOddRowAttributes;
$this->_arrRowInfo['strEvenRowAttributes'] = $strEvenRowAttributes;
$this->_arrRowInfo['strHoverRowClassName'] = $strHoverRowClassName;
// See if we can parse a class name out of the even and odd row attributes
$strLCase = strtolower($strOddRowAttributes);
$iPos = strpos($strLCase, 'class');
if ($iPos !== false) {
$strClass = substr($strOddRowAttributes, $iPos + 5);
$strClass = trim(str_replace("'", '', str_replace('"', '', str_replace('=', '', $strClass))));
$jPos = strpos($strClass, ' ');
if ($jPos !== false) { $strClass = substr($strClass, 0, $jPos); }
}
else { $strClass = ''; }
$this->_arrRowInfo['strOddRowClassName'] = $strClass;
$strLCase = strtolower($strEvenRowAttributes);
$iPos = strpos($strLCase, 'class');
if ($iPos !== false) {
$strClass = substr($strEvenRowAttributes, $iPos + 5);
$strClass = trim(str_replace("'", '', str_replace('"', '', str_replace('=', '', $strClass))));
$jPos = strpos($strClass, ' ');
if ($jPos !== false) { $strClass = substr($strClass, 0, $jPos); }
}
else { $strClass = ''; }
$this->_arrRowInfo['strEvenRowClassName'] = $strClass;
return;
}
/**
* Sets additional attributes (style/class tags, etc.) for the field/column headings of the report
* @param string $strAttributes Any additional attributes to use for displaying/formatting the field headings
* @return void
* @access public
*/
function setFieldHeadingAttributes($strAttributes)
{
$this->_arrTitleInfo['strFieldHeadingAttributes'] = $strAttributes;
return;
}
/**
* Allows you to specify whether or not to use hidden columns; in order to use them
* you MUST specify a non-empty CSS class name to use for the hidden columns. Suggested
* attributes for such a class would be "display: none; padding: 0; margin: 0;".
*
* @param $strHiddenColumnClassName string The CSS class name to use for hidden columns
* @return void
* @access public
*/
function setHiddenColumnClassName($strHiddenColumnClassName)
{
$this->_arrColumnInfo['strHiddenColumnClassName'] = $strHiddenColumnClassName;
return;
}
/**
* Configure linking for this report, to allow individual row values to link to other pages.
* Intended primarily for use in linking to sub-reports, or detailed views of data.
* @param boolean $blnAllowLink True/False for whether or not to allow linking from this report list
* @param string $strLinkURL Any URL information for the site to link to; valid examples
* include: "thisPage.php", "thisPage.php?SomeInfo=1&SomeOtherInfo=2", or
* "http://www.somesite.com/thisPage.php". For lists where the base URL is different for every
* record, you can include a variable URL by using %% variable indicators, such as:
* "%%strPageName%%.php" - in this case the value of the "strPageName" field for each
* record will be substituted into the Link URL.
* @param array $arrParamFields An array of information about which fields from the data
* behind this report list, should be used in constructing the row link. A valid array must
* include the following information: "name" the exact name of the field in the report's data
* array, and optionally "marker" which is the name to use for this parameter in the row link URL.
* For example: $arrParamFields = array(
* array('name'=>'Id', 'marker'='userId'),
* array('name'=>'Type', 'marker'=>'userType')
* ) would be a valid argument.
* @param string $strLinkAttributes Any additional attributes to be used in constructing the
* row link URL, e.g. style/class settings, target modifiers, etc.
* @param string $strLinkAnchor The HREF anchor for the link target
* @param string $strLinkCondition A conditional test for whether or not to display a link. Say, for
* example, if you only wanted to allow report drill-downs if a particular field was non-zero for instance.
* In such a case you might set the condition to 'intval(%%intTestField%%) != 0' which would evalulate to
* true for records that had a non-zero value in the field of interest (thus allowing a drill-down link)
* and false otherwise (which would keep any link from being displayed). * @return void
* @access public
*/
function allowLink($blnAllowLink, $strLinkURL, $arrParamFields, $strLinkAttributes = '', $strLinkAnchor = '', $strLinkCondition = '')
{
$this->_arrLinkInfo['blnAllowLink'] = $blnAllowLink;
$this->_arrLinkInfo['strLinkURL'] = $strLinkURL;
$this->_arrLinkInfo['arrLinkParamFields'] = $arrParamFields;
$this->_arrLinkInfo['strLinkAttributes'] = $strLinkAttributes;
$this->_arrLinkInfo['strLinkAnchor'] = $strLinkAnchor;
$this->_arrLinkInfo['strLinkCondition'] = $strLinkCondition;
return;
}
/**
* Configures pagination for the report list.
* @param boolean $blnAllowPaging True/False for whether or not to allow pagination of report results
* @param string $strPagingURL The URL to submit GET requests to when moving between pages
* @param string $strPagingAnchor The HREF anchor to use in pagination links
* @param string $blnShowPagingBottom True/False for whether or not to show a bottom set of pagination controls
* @return void
* @access public
*/
function allowPaging($blnAllowPaging, $strPagingURL, $strPagingAnchor = '', $blnShowPagingBottom = false)
{
$this->_arrPagingInfo['blnAllowPaging'] = (bool) $blnAllowPaging;
$this->_arrPagingInfo['strPagingURL'] = $strPagingURL;
$this->_arrPagingInfo['strPagingAnchor'] = trim($strPagingAnchor);
$this->_arrPagingInfo['blnShowPagingBottom'] = $blnShowPagingBottom;
return;
}
/**
* Configures column sorting for the report list.
* @param boolean $blnAllowSort True/False for whether or not to allow sorting of report results
* @param string $strSortField The name of the field to sort by
* @param boolean $blnSortDescending True/False for whether or not to sort in descending order
* by the specified field
* @param string $strSortURL The URL to submit GET requests to when a sort "command" is issued
* by clicking on any of the visible columns
* @param boolean $blnUsePathParams True/False for whether or not to use path syntax for parameter
* values instead of normal ?,&,= syntax (i.e. &Sort=SortField&SortDescending=1 vs.
* /Sort-SortField/SortDescending-1)
* @param string $strSortAnchor The HREF anchor to use in sort target links
* @return void
* @access public
*/
function allowSort($blnAllowSort, $strSortField, $blnSortDescending, $strSortURL, $blnUsePathParams = 0, $strSortAnchor = '', $strLinkAscClass = '', $strLinkDescClass = '')
{
// Make sure the specified URL does NOT have the Sort & SortDescending parameters
$iPos = strpos($strSortURL, '&Sort=');
if ($iPos === false) { $iPos = strpos($strSortURL, '?Sort='); }
if (!($iPos === false)) {
$jPos = strpos($strSortURL, '&', $iPos + 1);
if ($jPos === false) { $jPos = strlen($strSortURL); }
$strSortURL = substr($strSortURL, 0, $iPos) .
substr($strSortURL, $jPos);
}
$iPos = strpos($strSortURL, '&SortDescending=');
if ($iPos === false) { $iPos = strpos($strSortURL, '?SortDescending='); }
if (!($iPos === false)) {
$jPos = strpos($strSortURL, '&', $iPos + 1);
if ($jPos === false) { $jPos = strlen($strSortURL); }
$strSortURL = substr($strSortURL, 0, $iPos) .
substr($strSortURL, $jPos);
}
// For later simplicity force the URL to end with Sort= (or Sort- if we're using the path form of parametization)
if ($blnUsePathParams) {
if (substr($strSortURL, strlen($strSortURL) - 1, 1) === '/') {
$strSortURL .= 'Sort-';
}
else { $strSortURL .= '/Sort-'; }
}
else {
if (strpos($strSortURL, '?') === false) {
$strSortURL .= '?Sort=';
}
else { $strSortURL .= '&Sort='; }
}
$this->_arrSortInfo['blnAllowSort'] = (bool) $blnAllowSort;
$this->_arrSortInfo['strSortField'] = $strSortField;
$this->_arrSortInfo['blnSortDescending'] = intval($blnSortDescending);
$this->_arrSortInfo['strSortURL'] = $strSortURL;
$this->_arrSortInfo['blnUsePathParams'] = intval($blnUsePathParams);
if (trim($strSortAnchor) != '') {
$this->_arrSortInfo['strSortAnchor'] = '#' . trim($strSortAnchor);
}
$this->_arrSortInfo['strLinkAscClass'] = $strLinkAscClass;
$this->_arrSortInfo['strLinkDescClass'] = $strLinkDescClass;
return;
}
/**
* Legacy functionality. This allows you to specify that the list class should wrap all data in <nobr> tags
* to prevent any kind of line wrapping within cells.
* @param boolean $blnAllowNoBreaks Whether or not to wrap all data in <nobr> tags
* @return void
* @access public
*/
function allowNoBreaks($blnAllowNoBreaks)
{
$this->_blnDoNoBreaks = (bool) $blnAllowNoBreaks;
return;
}
function allowGridLayout($blnDoGridLayout, $blnShowFieldNames, $strFieldAttributes, $intRecordsPerRow)
{
$this->_arrGridLayoutInfo['blnDoMultiLineList'] = (bool) $blnDoGridLayout;
$this->_arrGridLayoutInfo['blnShowFieldNames'] = (bool) $blnShowFieldNames;
$this->_arrGridLayoutInfo['strFieldAttributes'] = trim($strFieldAttributes);
$this->_arrGridLayoutInfo['intRecordsPerRow'] = intval($intRecordsPerRow);
return;
}
/**
* Aids in pagination of report results by setting which record to begin showing results for, and how
* many records to show per page.
* @param integer $intStartRecord The number of the first record to display results for
* @param integer $intNumRecords The number of records to show per page (0 or less mean
* show all results on a single page
* @return void
* @access public
*/
function setListRange($intStartRecord, $intNumRecords)
{
$this->_arrPagingInfo['intStartRecord'] = intval($intStartRecord);
$this->_arrPagingInfo['intNumRecords'] = intval($intNumRecords);
return;
}
/**
* Sets whether or not to show column names above each column in the report.
* @param boolean $blnDoShow True/False for whether or not to show column names
* @return void
* @access public
*/
function showColumnNames($blnDoShow)
{
$this->_arrColumnInfo['blnShowColumnNames'] = $blnDoShow;
return;
}
/**
* Sets whether or not to show that fields having a max length have been truncated.
* Basically this sets whether or not to show an elipsis (...) if necessary
* @param boolean $blnDoShow True/False for whether or not to show truncation
* @return void
* @access public
*/
function showColumnTruncation($blnDoShow)
{
$this->_arrColumnInfo['blnIndicateTruncation'] = $blnDoShow;
return;
}
/**
* Resets column display information. Primarily used if the same list object is being used multiple
* times during the same page processing.
* @return void
* @access public
*/
function clearOutputColumns()
{
$this->_arrColumnInfo['blnUseAllColumns'] = true;
$this->_arrColumnInfo['arrColumnsToUse'] = array();
return;
}
/**
* Adds a particular field from the data as a visible (display or output) column in the report list.
* @param string $strFieldName The name (from data) of the field to show
* @param string $strColumnTitle The name to show as the title of this field in the output
* @param string $strFormat A named format type to use when outputting the value of this field.
* Supported options currently include: phone, ucase, lcase, pcase, decimal, money, dollars, date,
* datetime, and wrapped
* @param string $strAlign A horizontal alignment for the field (left, middle, right)
* @param integer $intLine For use with multi-line reports, this is what line of output this particular field
* should be displayed on
* @param string $strFieldLink Additional information/parameters to be included in the link that is constructed
* for values in this particular column. Only used when report linking is turned on (i.e. for sub-reports). Can
* also be set to the special flag value of "__NOLINK__" which will disable linking for this single column.
* @param string $strLinkDesc Similar to Alt tags for images, this value will be displayed as a pop-up information
* information display automatically (in supported browsers) when a link for this column is hovered over
* @param string $strAttribs Additional attributes to use for outputting this column such as CSS styles, etc.
* @param array $arrLinkInfo Custom link URL instructions for this column; if null then the default link URL
* instructions for the overall report (i.e. row-level instructions) are utilized for this column.
* @param string $strFormula Any formula (that can be interpreted in PHP using "eval") to use for the display
* value of this field. This can include conditional logic such as 'intval(%%intTestField%%) > 0 ? "Details" : ""'
* which could be used to display a particular sub-link only when some other column had a positive value
* @param boolean $blnIsHidden Whether or not to hide the column on initial display of the report; default is false
* @return void
* @access public
*/
function addOutputColumn($strFieldName, $strColumnTitle, $strFormat = null, $strAlign = null, $intLine = null, $strFieldLink = null, $strLinkDesc = null, $intMaxLength = null, $strAttribs = null, $arrLinkInfo = null, $strFormula = null, $blnIsHidden = null)
{
// Make sure we track that the user is customizing the output columns
$this->_arrColumnInfo['blnUseAllColumns'] = false;
$arrColumn['name'] = $strFieldName;
$arrColumn['title'] = $strColumnTitle;
$arrColumn['format'] = $strFormat;
$arrColumn['align'] = $strAlign;
$arrColumn['line'] = $intLine;
$arrColumn['fldlink'] = $strFieldLink;
$arrColumn['linkdesc'] = $strLinkDesc;
$arrColumn['maxlen'] = $intMaxLength;
$arrColumn['attribs'] = $strAttribs;
$arrColumn['linkinfo'] = $arrLinkInfo;
$arrColumn['formula'] = $strFormula;
$arrColumn['hidden'] = (bool) $blnIsHidden;
array_push($this->_arrColumnInfo['arrColumnsToUse'], $arrColumn);
return;
}
/**
* Provides a mechanism for adding columns to a list without individually specifying
* every single parameter to the addOutputColumn() in case you want to use one of the
* later parameters.
* @param $arrDetails array The array of information to use in configuring this column
* @return void
* @access public
*/
function addOutputColumnFromArray($arrDetails)
{
$strFieldName = (array_key_exists('name', $arrDetails) ? $arrDetails['name'] : '');
$strColumnTitle = (array_key_exists('title', $arrDetails) ? $arrDetails['title'] : '');
$strFormat = (array_key_exists('format', $arrDetails) ? $arrDetails['format'] : null);
$strAlign = (array_key_exists('align', $arrDetails) ? $arrDetails['align'] : null);
$intLine = (array_key_exists('line', $arrDetails) ? $arrDetails['line'] : null);
$strFieldLink = (array_key_exists('fldlink', $arrDetails) ? $arrDetails['fldlink'] : null);
$strLinkDesc = (array_key_exists('linkdesc', $arrDetails) ? $arrDetails['linkdesc'] : null);
$intMaxLength = (array_key_exists('maxlen', $arrDetails) ? $arrDetails['maxlen'] : null);
$strAttribs = (array_key_exists('attribs', $arrDetails) ? $arrDetails['attribs'] : null);
$arrLinkInfo = (array_key_exists('linkinfo', $arrDetails) ? $arrDetails['linkinfo'] : null);
$strFormula = (array_key_exists('formula', $arrDetails) ? $arrDetails['formula'] : null);
$blnIsHidden = (array_key_exists('hidden', $arrDetails) ? $arrDetails['hidden'] : null);
$this->addOutputColumn($strFieldName, $strColumnTitle, $strFormat, $strAlign, $intLine, $strFieldLink, $strLinkDesc, $intMaxLength, $strAttribs, $arrLinkInfo, $strFormula, $blnIsHidden);
return;
}
/**
* Provides a mechanism for updating a particular column detail after it has been
* added to the list
* @param string $strFieldName The name (from data) of the field to show
* @param string $strAttributeName The name of the piece of information to be modified
* for this column, e.g. title, format, align, etc.
* @param object $attributeValue The new value to use for this particular detail
* @return void
* @access public
*/
function setOutputColumnAttribute($strFieldName, $strAttributeName, $attributeValue)
{
$intColumnCount = count($this->_arrColumnInfo['arrColumnsToUse']);
for ($i = 0; $i < $intColumnCount; $i++) {
if ($this->_arrColumnInfo['arrColumnsToUse'][$i]['name'] == $strFieldName) {
$this->_arrColumnInfo['arrColumnsToUse'][$i][$strAttributeName] = $attributeValue;
break;
}
}
return;
}
/**
* Configure which data fields/columns will serve as a "key" value in deciding which records
* belong in which section of the report.
* @param string $strFieldNamesCommaSep a comma-delimited string of field names that
* should comprise the "key" value for each section (ex: "lngRecordId,intRecordType")
* @return void
* @access public
*/
function setSectionBreakFields($strFieldNamesCommaSep)
{
$strFieldNamesCommaSep = trim($strFieldNamesCommaSep);
if ($strFieldNamesCommaSep == '') {
$this->_arrColumnInfo['arrSectionKeys'] = array();
}
else {
$strFieldNamesCommaSep = eregi_replace(' ', '', $strFieldNamesCommaSep);
$this->_arrColumnInfo['arrSectionKeys'] = explode(',', $strFieldNamesCommaSep);
}
return;
}
/**
*
* @param boolean $blnShowOverallSummary True/False for whether or not to show overall summaries, e.g.
* calculated totals for the entire set of data. Overall summaries
* are shown on each page of results, when pagination is used.
* @param boolean $blnShowPageLevelSummary True/False for whether or not to show calculated summaries
* for each page of results.
* @param array $arrSummaryFields Information about what to show as the summary row(s) for this
* report. Structure is as the following:
* $arrSummaryFields = array( $arrFieldDef1, $arrFieldDef2, ... );
*
* $arrFieldDef = array( $strFieldName, $strFunction, $strFunctionArgs );
*
* EXAMPLE:
* $arrSummaryFields = array(
* array('strFieldName1', 'custom', 'TOTALS'),
* array('strFieldName2', 'count-all', 'lngRecordId'),
* array('strFieldName3', 'sum', 'curAmountSpent'),
* );
* @return void
* @access public
*/
function setSummaryRow($blnShowOverallSummary, $blnShowPageLevelSummary, $arrSummaryFields, $strOverallSummaryAttributes = null, $strPageLevelSummaryAttributes = null)
{
$this->_arrSummaryInfo['blnShowOverallSummary'] = (bool) $blnShowOverallSummary;
$this->_arrSummaryInfo['blnShowPageLevelSummary'] = (bool) $blnShowPageLevelSummary;
$this->_arrSummaryInfo['arrSummaryFields'] = $arrSummaryFields;
$this->_arrSummaryInfo['strOverallSummaryAttributes'] = $strOverallSummaryAttributes;
$this->_arrSummaryInfo['strPageLevelSummaryAttributes'] = $strPageLevelSummaryAttributes;
return;
}
/**
* A public accessor to our internal formatting function.
* @param string $strValue The data value/input to be formatted
* @param string $strFormat The named format to use for outputting the value to be displayed
* @return void
* @access public
*/
function formatValue($strValue, $strFormat)
{
return($this->_formatListValue($strValue, strtolower($strFormat)));
}
/**
* Executes the specified SQL statement against the specified database, and directly outputs
* the results of the report list constructed from the results as a download to the client browser.
* Because this routine controls the headers that are sent to the client browser, no other header
* information should be sent prior to executing this routine (unless output buffering is enabled).
* @param object $pearDB An open PEAR database connection to a database
* @param string $strSQL The desired SQL statement(s) to execute to produce the report list data
* @param object $arrParams Any parameter information that is needed to execute the above SQL statement
* @param string $strOutputMethod The desired output format of the report. Currently supported options
* are: html (default, also works well for MS Word downloads), tab (tab-delimited, as for MS Excel
* downloads), csv (comma separated), and xml.
* @return void
* @access public
*/
function downloadListFromSQL($pearDB, $strSQL, $arrParams = null, $strOutputMethod = null)
{
// Execute the SQL statement and get an array of the results
$arrResult = $pearDB->getAll($strSQL, $arrParams, DB_FETCHMODE_ASSOC);
if (DB::isError($arrResult)) {
$arrResult = array();
}
$this->downloadListFromArray($arrResult, $strOutputMethod);
return;
}
/**
* Builds and directly initiates a download of a report list from the specified array of data. Because this routine
* controls the headers that are sent to the client browser, no other header information should be sent
* prior to executing this routine (unless output buffering is enabled).
* @param array $arrRecords The array of data to build a report list for
* @param string $strOutputMethod The desired output format of the report. Currently supported options
* are: html (default, also works well for MS Word downloads), tab (tab-delimited, as for MS Excel
* downloads), csv (comma separated), and xml.
* @return void
* @access public
*/
function downloadListFromArray($arrRecords, $strOutputMethod = null)
{
$strDownloadName = trim($this->_downloadName);
if ($strDownloadName == '') { $strDownloadName = 'ReportList_' . rand(1, 9); }
$strDownloadType = trim($this->_downloadType);
// See if any particular download type was specified; if not default to one based on the output method
if ($strDownloadType == '') {
switch ($strOutputMethod) {
case 'tab' :
$strDownloadType = 'txt';
break;
case 'csv' :
$strDownloadType = 'csv';
break;
case 'qcsv' :
$strDownloadType = 'qcsv';
break;
case 'html' :
$strDownloadType ='doc';
break;
case 'xml' :
$strDownloadType = 'xml';
break;
default :
$strDownloadType = 'xls';
$strOutputMethod = 'tab';
break;
}
}
// Determine how we should mark the download type
switch ($strDownloadType) {
case 'txt' :
$strType = 'text/plain';
$strName = $strDownloadName . ".txt";
break;
case 'csv' :
case 'qcsv' :
$strType = 'text/plain';
$strName = $strDownloadName . ".csv";
break;
case 'doc' :
$strType = 'application/msword';
$strName = $strDownloadName . ".doc";
break;
case 'xml' :
$strType = 'text/plain';
$strName = $strDownloadName . ".xml";
break;
default :
$strType = 'application/msexcel';
$strName = $strDownloadName . ".xls";
break;
}
// Send directly back to the client; set the document type and suggested name
header("Content-Type: $strType");
header("Content-Disposition: attachment; filename=$strName\r\n\r\n");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Pragma: public");
echo $this->getListFromArray($arrRecords, true, $strOutputMethod);
return;
}
/**
* Executes the specified SQL statement against the specified database, and directly outputs
* the results of the report list constructed from the results.
* @param object $pearDB An open PEAR database connection to a database
* @param string $strSQL The desired SQL statement(s) to execute to produce the report list data
* @param object $arrParams Any parameter information that is needed to execute the above SQL statement
* @param string $strOutputMethod The desired output format of the report. Currently supported options
* are: html (default, also works well for MS Word downloads), tab (tab-delimited, as for MS Excel
* downloads), csv (comma separated), and xml.
* @return void
* @access public
*/
function makeListFromSQL($pearDB, $strSQL, $arrParams = null, $strOutputMethod = null)
{
echo $this->getListFromSQL($pearDB, $strSQL, $arrParams, false, $strOutputMethod);
return;
}
/**
* Executes the specified SQL statement against the specified database, and returns a report list
* constructed from the subsequent results.
* @param object $pearDB An open PEAR database connection to a database
* @param string $strSQL The desired SQL statement(s) to execute to produce the report list data
* @param object $arrParams Any parameter information that is needed to execute the above SQL statement
* @param boolean $blnDoForDownload True/False for whether it is intended the results of this report
* will be used in a download (offline) file. This will affect whether record linking, page navigation, etc.
* is available
* @param string $strOutputMethod The desired output format of the report. Currently supported options
* are: html (default, also works well for MS Word downloads), tab (tab-delimited, as for MS Excel
* downloads), csv (comma separated), and xml.
* @return string The fully formatted list to output
* @access public
*/
function getListFromSQL($pearDB, $strSQL, $arrParams = null, $blnDoForDownload = false, $strOutputMethod = null)
{
// Execute the SQL statement and get an array of the results
$arrResult = $pearDB->getAll($strSQL, $arrParams, DB_FETCHMODE_ASSOC);
if (DB::isError($arrResult)) {
$arrResult = array();
}
// Return the actual list that gets built from the queried data
return($this->getListFromArray($arrResult, $blnDoForDownload, $strOutputMethod));
}
/**
* Builds and directly outputs a report list from the specified array of data.
* @param array $arrRecords The array of data to build a report list for
* @param string $strOutputMethod The desired output format of the report. Currently supported options
* are: html (default, also works well for MS Word downloads), tab (tab-delimited, as for MS Excel
* downloads), csv (comma separated), and xml.
* @return void
* @access public
*/
function makeListFromArray($arrRecords, $strOutputMethod = null)
{
echo $this->getListFromArray($arrRecords, false, $strOutputMethod);
return;
}
/**
* This is the primary workhorse routine of the entire class, and produces a string (containing the
* entire report list output) for the given array of data.
* @param array $arrRecords The data array for which to build the report list
* @param boolean $blnDoForDownload True/False for whether or not the list is intended for
* download (offline) viewing. This wil affect the availability/use of page navigators, links, etc.
* @param string $strOutputMethod The desired output format of the report. Currently supported options
* are: html (default, also works well for MS Word downloads), tab (tab-delimited, as for MS Excel
* downloads), csv (comma separated), qcsv (quoted, comma separated), and xml.
* @return string The fully formatted list to output
* @access public
*/
function getListFromArray($arrRecords, $blnDoForDownload = false, $strOutputMethod = null)
{
$this->_intControlId = 0;
$this->_blnRowIsOdd = true;
$this->_strReportId = trim(intval($this->_strReportId) + 1);
$blnUseHiddenColumns = false;
// Are we doing a standard list or a multi-line list (e.g. grid layout)?
$blnDoMultiLineList = $this->_arrGridLayoutInfo['blnDoMultiLineList'];
// Default to outputting html
if ($strOutputMethod === null) { $strOutputMethod = 'html'; }
// Start with an empty list
$strList = '';
$intTotalRecords = count($arrRecords);
if ($intTotalRecords == 0) {
// No records to work with
$strList .= $this->_makeListHeader($strOutputMethod);
$strList .= $this->_makeListCaption($this->_strListTitle, $strOutputMethod);
$strList .= $this->_makeListTitles('<th>No records returned.</th>', $strOutputMethod);
$strList .= $this->_makeListFooter($strOutputMethod);
return($strList);
}
// Figure out how many of the results we're actually supposed to show
$intFirstRecord = $this->_arrPagingInfo['intStartRecord'];
if ($this->_arrPagingInfo['intNumRecords'] <= 0) {
// Show all the records
$intLastRecord = $intTotalRecords;
}
else {
// Show only the specified number of records per page
$intLastRecord = $intFirstRecord + $this->_arrPagingInfo['intNumRecords'];
if ($intLastRecord > $intTotalRecords) { $intLastRecord = $intTotalRecords; }
}
// Sort results before displaying, if necessary
if ($this->_arrSortInfo['blnAllowSort'] && $this->_arrSortInfo['strSortField'] != '') {
// Sort the array on the specified value
foreach ($arrRecords as $curRecord) {
$varTest = $curRecord[$this->_arrSortInfo['strSortField']];
if (is_string($varTest)) {
$sortarray[] = strtolower($varTest);
}
else { $sortarray[] = $varTest; }
}
array_multisort($sortarray, $arrRecords);
// Sort descending, if necessary
if ($this->_arrSortInfo['blnSortDescending']) {
$arrRecords = array_reverse($arrRecords);
}
}
// Which and how many columns are we using?
$arrColumnsToUse = array();
$this->_determineColumnsToUse($arrRecords[0], $arrColumnsToUse);
$intNumCols = count($arrColumnsToUse);
// Are we going to use hidden columns?
if ($strOutputMethod == 'html' && !($blnDoMultiLineList) && !($blnDoForDownload)) {
if ($this->_arrColumnInfo['strHiddenColumnClassName'] != '') {
foreach ($arrColumnsToUse as $arrCol) {
if ($arrCol['hidden']) {
$blnUseHiddenColumns = true;
break;
}
}
}
}
// Start writing our list
$strList .= $this->_makeListHeader($strOutputMethod);
// Show a title for the list if possible
if ($strOutputMethod == 'xml') {
$strHeading = '';
if (trim($this->_arrTitleInfo['strListTitle']) != '') {
$strHeading .= "\t<listTitle>" . trim($this->_arrTitleInfo['strListTitle']) . "</listTitle>\r\n";
}
if (trim($this->_arrTitleInfo['strListSubTitle']) != '') {
$strHeading .= "\t<listSubTitle>" . trim($this->_arrTitleInfo['strListSubTitle']) . "</listSubTitle>\r\n";
}
if ($this->_arrTitleInfo['blnShowRecordCount']) {
$strHeading .= "\t<listCaption>Record(s) " . ($intFirstRecord + 1) . " to $intLastRecord of $intTotalRecords total.</listCaption>\r\n";
}
if ($strHeading != '') {
$strList .= "<ListDescription>\r\n" .
$strHeading .
"</ListDescription>\r\n";
}
}
else {
$strCaption = trim($this->_arrTitleInfo['strTitleOpenAttributes'] . $this->_arrTitleInfo['strListTitle'] . $this->_arrTitleInfo['strTitleCloseAttributes']);
if (trim($this->_arrTitleInfo['strListSubTitle']) != '') { $strCaption .= '<br />' . trim($this->_arrTitleInfo['strSubTitleOpenAttributes'] . $this->_arrTitleInfo['strListSubTitle'] . $this->_arrTitleInfo['strSubTitleCloseAttributes']); }
if ($this->_arrTitleInfo['blnShowRecordCount']) {
if ($strCaption != '') { $strCaption .= '<br />'; }
// Only show the record counts if we're doing a list w/out section breaks
// Otherwise, show the section heading
if ((count($this->_arrColumnInfo['arrSectionKeys']) == 0) || (!($blnDoForDownload)) || ($strOutputMethod == 'tab')) {
$strCaption .= 'Record(s) ' . ($intFirstRecord + 1) . " to $intLastRecord of $intTotalRecords total.";
}
else { $strCaption .= 'Section: %%SECTION_KEY%%'; }
}
$strList .= $this->_makeListCaption($strCaption, $strOutputMethod);
}
// If all the records won't fit on one page, show the necessary
// navigation controls (if allowed by the caller)
if (!($blnDoForDownload) && $this->_arrPagingInfo['blnAllowPaging']) {
if ($blnDoMultiLineList) {
$strList .= $this->_makePageNavigators($intTotalRecords, $this->_arrGridLayoutInfo['intRecordsPerRow']);
}
else {
$strList .= $this->_makePageNavigators($intTotalRecords, $intNumCols + ($blnUseHiddenColumns ? 1 : 0));
}
}
// If necessary, show controls for hiding/showing columns
if ($blnUseHiddenColumns) {
$strRow = "<td colspan=\"" . ($intNumCols + 1) . "\" align=\"left\">";
$strRow .= '<span id="rptlist_' . $this->_strReportId . '_ctl_0" style="display: none;"><a href="javascript: void 0;" onclick="toggleColumns_' . $this->_strReportId . '(false);">Show more columns >></a></span>' .
'<span id="rptlist_' . $this->_strReportId . '_ctl_1" style="display: none;"><a href="javascript: void 0;" onclick="toggleColumns_' . $this->_strReportId . '(true);"><< Show fewer columns</a></span>';
$strRow = $this->_makeListTitles($strRow . '</td>');
$strRow = str_replace('<tr ' . $this->_arrTitleInfo['strFieldHeadingAttributes'] . '>', '<tr ' . $this->_arrTitleInfo['strPageNavigatorAttributes'] . '>', $strRow);
$strList .= $strRow;
}
// Show the column headings (titles) if allowed
if (!($blnDoMultiLineList) && $this->_arrColumnInfo['blnShowColumnNames'] && ($strOutputMethod != 'xml')) {
$strRow = '';
foreach ($arrColumnsToUse as $arrCol) {
// Handle cell alignment if necessary
if ($arrCol['align'] != '') {
$strRow .= '<th align="' . $arrCol['align'] . '"';
}
else { $strRow .= '<th'; }
// Handle hidden columns
if ($blnUseHiddenColumns && $arrCol['hidden']) {
$strRow .= ' id="rptlist_' . $this->_strReportId . '_' . $this->_intControlId . '">';
$this->_intControlId++;
}
else {
$strRow .= '>';
}
$strColTitle = $arrCol['title'];
// Show sort order links
if ($this->_arrSortInfo['blnAllowSort'] && !($blnDoForDownload)) {
if ($arrCol['name'] == $this->_arrSortInfo['strSortField'] && $this->_arrSortInfo['blnSortDescending']) {
// Give the option to sort ascending
if ($this->_arrSortInfo['blnUsePathParams'] == 1) {
$strColTitle = '<a href="' . $this->_arrSortInfo['strSortURL'] . urlencode($arrCol['name']) . '/SortDescending-0" title="Click here to sort (in ascending order) by ' . htmlspecialchars(strip_tags($strColTitle)) . '" ' . $this->_arrSortInfo['strLinkAscClass'] . '>' . $strColTitle . '</a>';
}
else {
$strColTitle = '<a href="' . $this->_arrSortInfo['strSortURL'] . urlencode($arrCol['name']) . '&SortDescending=0' . $this->_arrSortInfo['strSortAnchor'] . '" title="Click here to sort (in ascending order) by ' . htmlspecialchars(strip_tags($strColTitle)) . '" ' . $this->_arrSortInfo['strLinkAscClass'] . '>' . $strColTitle . '</a>';
}
}
else {
// Give the option to sort (decending) by this field
if ($this->_arrSortInfo['blnUsePathParams'] == 1) {
$strColTitle = '<a href="' . $this->_arrSortInfo['strSortURL'] . urlencode($arrCol['name']) . '/SortDescending-1" title="Click here to sort (in descending order) by ' . htmlspecialchars(strip_tags($strColTitle)) . '" ' . $this->_arrSortInfo['strLinkDescClass'] . '>' . $strColTitle . '</a>';
}
else {
$strColTitle = '<a href="' . $this->_arrSortInfo['strSortURL'] . urlencode($arrCol['name']) . '&SortDescending=1' . $this->_arrSortInfo['strSortAnchor'] . '" title="Click here to sort (in descending order) by ' . htmlspecialchars(strip_tags($strColTitle)) . '" ' . $this->_arrSortInfo['strLinkDescClass'] . '>' . $strColTitle . '</a>';
}
}
}
// Allow for setting "no break" tags within cells
if ($this->_blnDoNoBreaks){
$strRow .= '<nobr>' . $strColTitle . '</nobr></th>';
}
else { $strRow .= $strColTitle . '</th>'; }
}
$strList .= $this->_makeListTitles($strRow, $strOutputMethod);
}
// Save the list header so far, in case we need to use it again later, as
// in MS Word downloads with section breaks
$strListHeader = $strList;
// Show each record
$strRow = '';
$strSectionKey = '';
$intRecordCount = 1;
for ($iRow = $intFirstRecord; $iRow < $intLastRecord; $iRow++) {
$strRow .= $this->_buildListRow( $arrColumnsToUse,
$blnDoForDownload,
$strOutputMethod,
$blnDoMultiLineList,
$blnUseHiddenColumns,
$strListHeader,
$iRow,
$arrRecords[$iRow],
$strSectionKey
);
if ($blnDoMultiLineList && $strOutputMethod == 'html') {
if ($intRecordCount >= $this->_arrGridLayoutInfo['intRecordsPerRow']) {
$strList .= $this->_makeListRow($strRow, $strOutputMethod);
$intRecordCount = 1;
$strRow = '';
}
else { $intRecordCount++; }
}
else {
// Ouput this record to our list
$strList .= $this->_makeListRow($strRow, $strOutputMethod);
$strRow = '';
}
}
if ($blnDoMultiLineList && $strOutputMethod == 'html') {
if ($strRow != '') {
// Pad this row with empty values for any missing columns
$arrTempRecord = array();
$arrTempColumns = array( array( 'name' => 'strReportListGridLayoutPlaceholder',
'title' => '',
'format' => '',
'align' => '' ) );
$strSectionKey = '';
for ($iRow = $intRecordCount; $iRow <= $this->_arrGridLayoutInfo['intRecordsPerRow']; $iRow++) {
$strRow .= $this->_buildListRow( $arrTempColumns,
$blnDoForDownload,
$strOutputMethod,
$blnDoMultiLineList,
$blnUseHiddenColumns,
$strListHeader,
0,
$arrTempRecord,
$strSectionKey
);
}
// Show the last bit of multi-line data
$strList .= $this->_makeListRow($strRow, $strOutputMethod);
}
}
// ***
// Summary rows
if (!($blnDoForDownload) && $this->_arrPagingInfo['blnAllowPaging']) {
// Only show the page-level summary if we're actually doing pagination, and there's more
// than one page of results (and if requested)
if ($intFirstRecord != 0 || $intLastRecord != $intTotalRecords) {
if ($this->_arrSummaryInfo['blnShowPageLevelSummary']) {
$strRow = $this->_makeListSummaryRow( $arrColumnsToUse,
$blnDoForDownload,
$strOutputMethod,
$blnDoMultiLineList,
$blnUseHiddenColumns,
$strListHeader,
$arrRecords,
$intFirstRecord,
$intLastRecord,
true
);
$strList .= $this->_makeListRow($strRow, $strOutputMethod);
}
}
}
if ($this->_arrSummaryInfo['blnShowOverallSummary']) {
$strRow = $this->_makeListSummaryRow( $arrColumnsToUse,
$blnDoForDownload,
$strOutputMethod,
$blnDoMultiLineList,
$blnUseHiddenColumns,
$strListHeader,
$arrRecords,
0,
$intTotalRecords,
false
);
$strList .= $this->_makeListRow($strRow, $strOutputMethod);
}
// ***
// If necessary, show a bottom set of controls for hiding/showing columns
if ($blnUseHiddenColumns && $this->_arrPagingInfo['blnShowPagingBottom']) {
$strRow = "<td colspan=\"" . ($intNumCols + 1) . "\" align=\"left\">";
$strRow .= '<span id="rptlist_' . $this->_strReportId . '_ctl_2" style="display: none;"><a href="javascript: void 0;" onclick="toggleColumns_' . $this->_strReportId . '(false);">Show more columns >></a></span>' .
'<span id="rptlist_' . $this->_strReportId . '_ctl_3" style="display: none;"><a href="javascript: void 0;" onclick="toggleColumns_' . $this->_strReportId . '(true);"><< Show fewer columns</a></span>';
$strRow = $this->_makeListTitles($strRow . '</td>');
$strRow = str_replace('<tr ' . $this->_arrTitleInfo['strFieldHeadingAttributes'] . '>', '<tr ' . $this->_arrTitleInfo['strPageNavigatorAttributes'] . '>', $strRow);
$strList .= $strRow;
}
// If necessary (and requested) show a bottom set of pagination controls
if (!($blnDoForDownload) && $this->_arrPagingInfo['blnAllowPaging'] && $this->_arrPagingInfo['blnShowPagingBottom']) {
if ($blnDoMultiLineList) {
$strList .= $this->_makePageNavigators($intTotalRecords, $this->_arrGridLayoutInfo['intRecordsPerRow']);
}
else {
$strList .= $this->_makePageNavigators($intTotalRecords, $intNumCols + ($blnUseHiddenColumns ? 1 : 0));
}
}
// Finish up our list
$strList .= $this->_makeListFooter($strOutputMethod, $blnUseHiddenColumns);
// If necessary, show a disclaimer at the bottom of the report
if (($blnDoForDownload) && ($this->_arrTitleInfo['strSubTitle'] != '') && ($strOutputMethod != 'xml')) {
if ($strOutputMethod == 'html') { $strList .= '<p>'; }
$strList .= "\r\n" . $this->_arrTitleInfo['strSubTitle'];
}
if ($blnDoForDownload && ($strOutputMethod == 'html')) {
// Doing a download to MS Word; include some style attributes that
// will help the report look a little better on initial download
$strList = $this->_strHtmlDownloadHeader . "\r\n" .
$strList . "\r\n" .
$this->_strHtmlDownloadFooter;
}
return($strList);
}
/* --- Private Methods --- */
function _makeListSummaryRow( $arrColumnsToUse,
$blnDoForDownload,
$strOutputMethod,
$blnDoMultiLineList,
$blnUseHiddenColumns,
$strListHeader,
$arrRecords,
$intFirstRecord,
$intLastRecord,
$blnPageTotals
)
{
$arrSummaryRecord = array();
$arrSummaryFields = $this->_arrSummaryInfo['arrSummaryFields'];
$intSummaryFieldCount = count($arrSummaryFields);
if ($intSummaryFieldCount == 0) { return(''); }
// Initialize all summary fields
foreach ($arrSummaryFields as $arrField) {
switch (strtolower($arrField[1])) {
case 'custom':
// User-specified values (typically labels, or the result of a complex calculation)
// These can be specified individually for overall versus page-level totals by passing
// an array instead of a value, e.g. array('Totals', 'Page Sub-Totals')
if (is_array($arrField[2])) {
$arrValues = $arrField[2];
if ($blnPageTotals) {
$arrSummaryRecord[$arrField[0]] = $arrValues[1];
}
else { $arrSummaryRecord[$arrField[0]] = $arrValues[0]; }
}
else { $arrSummaryRecord[$arrField[0]] = $arrField[2]; }
break;
case 'count-distinct':
// Distinct counts will require the use of an array
$arrSummaryRecord[$arrField[0]] = array();
break;
default:
// Everything else just gets initialized to 0
$arrSummaryRecord[$arrField[0]] = 0;
break;
}
}
// Now take care of the simple arithmetic values
for ($i = $intFirstRecord; $i < $intLastRecord; $i++) {
foreach ($arrSummaryFields as $arrField) {
switch (strtolower($arrField[1])) {
case 'count-all':
$arrSummaryRecord[$arrField[0]] += 1;
break;
case 'count-distinct':
// Build an array keyed off the designated field - when we're done all we
// have to do is count the elements in the array
$arrSummaryRecord[$arrField[0]][$arrRecords[$i][$arrField[2]]] = 1;
break;
case 'avg':
// Deliberate fall-through to summation logic
case 'sum':
$arrSummaryRecord[$arrField[0]] += $arrRecords[$i][$arrField[2]];
break;
default:
break;
}
}
}
// Now handle the more complex functions
foreach ($arrSummaryFields as $arrField) {
switch (strtolower($arrField[1])) {
case 'count-distinct':
// The current value in this field is an array of all distinct values counted
// Just reset the value to indicate the number of elements in this array
$arrSummaryRecord[$arrField[0]] = count($arrSummaryRecord[$arrField[0]]);
break;
case 'avg':
// The current value in this field is already the summation of all necessary data
// Here we just need to do the division
$intRecordCount = $intLastRecord - $intFirstRecord;
if ($intRecordCount > 0) {
$arrSummaryRecord[$arrField[0]] = $arrSummaryRecord[$arrField[0]] / (1.0 * $intRecordCount);
}
else { $arrSummaryRecord[$arrField[0]] = 0; }
break;
case 'percentage':
$arrColumns = $arrField[2];
$strNumField = $arrColumns[0];
$strDenomField = $arrColumns[1];
if ($arrSummaryRecord[$strDenomField] != 0) {
$arrSummaryRecord[$arrField[0]] = (100.0 * $arrSummaryRecord[$strNumField]) / (1.0 * $arrSummaryRecord[$strDenomField]);
}
else { $arrSummaryRecord[$arrField[0]] = 0.0; }
default:
break;
}
}
// Temporarily override column attributes with summary information
$arrDefaultColumnInfo = $arrColumnsToUse;
if ($blnPageTotals) {
$strAttribs = trim($this->_arrSummaryInfo['strPageLevelSummaryAttributes']);
}
else { $strAttribs = trim($this->_arrSummaryInfo['strOverallSummaryAttributes']); }
if ($strAttribs != '') {
for ($i = 0; $i < count($arrColumnsToUse); $i++) {
$arrColumnsToUse[$i]['attribs'] = $strAttribs;
}
}
// If we're doing a page-level summary, temporarily disable any record linking,
// as there's no way to ensure accurate construction of the links here
$blnDefaultLinkInfo = $this->_arrLinkInfo['blnAllowLink'];
if ($blnPageTotals) {
$this->_arrLinkInfo['blnAllowLink'] = false;
}
// Build the string representing this row of data
$strSectionKey = '';
$strRow = $this->_buildListRow( $arrColumnsToUse,
$blnDoForDownload,
$strOutputMethod,
$blnDoMultiLineList,
$blnUseHiddenColumns,
$strListHeader,
-1,
$arrSummaryRecord,
$strSectionKey
);
// Reset column attributes to original values
$arrColumnsToUse = $arrDefaultColumnInfo;
$this->_arrLinkInfo['blnAllowLink'] = $blnDefaultLinkInfo;
return($strRow);
}
function _buildListRow( $arrColumnsToUse,
$blnDoForDownload,
$strOutputMethod,
$blnDoMultiLineList,
$blnUseHiddenColumns,
$strListHeader,
$iRow,
$arrRecord,
&$strSectionKey
)
{
$strRow = '';
// If we allow linking from this list, then build our full hyperlink URL for this record here
if ($this->_arrLinkInfo['blnAllowLink']) {
$strRowHyperlink = $this->_buildLinkURLFromRecord($this->_arrLinkInfo, $arrRecord, false);
}
else { $strRowHyperlink = ''; }
// Watch for section breaks when doing html downloads (intended for MS Word)
if ((count($this->_arrColumnInfo['arrSectionKeys']) > 0) && ($blnDoForDownload) && !($strOutputMethod == 'tab')) {
if ($iRow > $this->_arrPagingInfo['intStartRecord']) {
$strCurSection = $this->_getSectionKeyValue($arrRecord);
if ($strCurSection != $strSectionKey) {
// Start a new section
$strList .= $this->_makeListFooter($strOutputMethod) .
"<br /><br clear=all style='mso-special-character:line-break;page-break-before:always' />\r\n" .
ereg_replace('%%SECTION_KEY%%', $strCurSection, $strListHeader);
$strSectionKey = $strCurSection;
$this->_blnRowIsOdd = false;
}
}
else {
$strSectionKey = $this->_getSectionKeyValue($arrRecord);
$strList = ereg_replace('%%SECTION_KEY%%', $strSectionKey, $strList);
}
}
// Cycle through each column that we're supposed to show and build a string representing that set of data
$iLine = 0;
$strRow = '';
foreach ($arrColumnsToUse as $arrCol) {
// Do any necessary formatting of the value
$strValue = $arrRecord[$arrCol['name']];
if ($arrCol['formula'] != '') {
$strTest = $arrCol['formula'];
if (strpos($strTest, '%%') !== false) {
foreach ($arrRecord as $key=>$val) {
$strTest = str_replace('%%' . $key . '%%', $val, $strTest);
}
}
$strValue = eval('return(' . $strTest . ');');
}
$strValue = $this->_formatListValue($strValue, $arrCol['format']);
// If a maximum length of the field value has been specified, then enforce that now
if ($arrCol['maxlen'] !== null) {
if (strlen($strValue) > $arrCol['maxlen']) {
$strValue = substr($strValue, 0, $arrCol['maxlen']);
if ($this->_arrColumnInfo['blnIndicateTruncation']) { $strValue .= '...'; }
}
}
if ($strOutputMethod == 'html') {
// Don't allow blanks (they make HTML tables look bad)
if ($strValue === '') { $strValue = ' '; }
}
else if ($strOutputMethod == 'csv') {
// In CSV downloads, you need to watch out for embedded commas or quotes
if (strpos($strValue, ',') !== false || strpos($strValue, '"') !== false) {
$strValue = '"' . $strValue . '"';
}
}
else if ($strOutputMethod == 'qcsv') {
// In Quoted-CSV downloads, you need to watch out for embedded quotes
$strValue = str_replace('"', '', $strValue);
}
else if ($strOutputMethod == 'xml') {
// Enclose each value in the XML tags necessary for this field
if ($arrCol['title'] != '') {
// The above IF implies that only fields with non-empty titles will be included
// in XML output; if you set a blank title, the field will be skipped
$strNodeName = $this->_getXmlNodeName($arrCol['title']);
$strValue = '<' . $strNodeName . '>' . htmlspecialchars(strip_tags($strValue)) . '</' . $strNodeName . '>';
}
}
if (!($blnDoForDownload)) {
if ($arrCol['linkinfo'] != null) {
// If this particular column uses a different link URL then rebuild our hyperlink
$strFieldHyperlink = $this->_buildLinkURLFromRecord($arrCol['linkinfo'], $arrRecord, false);
$strFieldLinkAttributes = $arrCol['linkinfo']['strLinkAttributes'];
}
else {
// Otherwise, just use the base URL for this entire row
$strFieldHyperlink = $strRowHyperlink;
$strFieldLinkAttributes = $this->_arrLinkInfo['strLinkAttributes'];
}
// If we allow linking from this page to another, then add the link here
if ($strFieldHyperlink != '' && $arrCol['fldlink'] != '__NOLINK__') {
// Show "alt" tags (i.e. descriptions/titles) for links
if ($arrCol['linkdesc'] != '') {
$strTitle = $arrCol['linkdesc'];
// See if there are any variables to replace in this title
if (strpos($strTitle, '%%') !== false) {
foreach ($arrColumnsToUse as $arrColReplace) {
$strValueReplace = $arrRecord[$arrColReplace['name']];
$strValueReplace = $this->_formatListValue($strValueReplace, $arrColReplace['format']);
$strTitle = str_replace('%%' . $arrColReplace['name'] . '%%', $strValueReplace, $strTitle);
}
}
$strTitle = htmlspecialchars($strTitle);
$strTitle = ' title="' . $strTitle . '"';
}
else { $strTitle = ''; }
$strValue = "<a href=$strFieldHyperlink" . $arrCol['fldlink'] . "\"$strTitle " . $strFieldLinkAttributes . ">$strValue</a>";
}
}
if ($blnDoMultiLineList) {
// New line, or the same one?
if ($strRow == '') { $strRow = "<td {$this->_arrGridLayoutInfo['strFieldAttributes']}>"; }
while ($iLine != $arrCol['line']) {
$strRow .= '<br />';
$iLine++;
}
// Add the value itself
if ($this->_arrGridLayoutInfo['blnShowFieldNames'] && $arrCol['title'] != '') {
$strRow .= '<strong><em>' . $arrCol['title'] . ':</em></strong> ' . $strValue . "\t";
}
else {
$strRow .= $strValue . "\t";
}
}
else {
// Standard, one-record-per-row type of list/report
// Handle cell alignment if necessary
$strColAttribs = trim($arrCol['attribs']);
if ($strColAttribs != '') { $strColAttribs = ' ' . $strColAttribs; }
if (trim($arrCol['align']) != '') {
$strRow .= '<td align="' . $arrCol['align'] . '"' . $strColAttribs;
}
else { $strRow .= '<td' . $strColAttribs; }
if ($blnUseHiddenColumns && $arrCol['hidden']) {
$strRow .= ' id="rptlist_' . $this->_strReportId . '_' . $this->_intControlId . '">';
$this->_intControlId++;
}
else {
$strRow .= '>';
}
// Allow for using the <nobr> tag
if ($this->_blnDoNoBreaks) {
$strRow .= "<nobr>$strValue</nobr></td>";
}
else { $strRow .= "$strValue</td>"; }
}
}
if ($blnDoMultiLineList) { $strRow .= '<br /> </td>'; }
return($strRow);
}
/**
* Determines which columns/fields should be used for outputting the report list
* results; mainly useful in the case where no particular output columns have been
* specified.
* @param array $arrRecord A sample record (the first in the dataset)
* which can be used to determine the available columns
* @param output array $arrColumnsToUse The actual array of column
* information which will be used for displaying the report. This array will
* contain one row for every column to output, with the following fields:
* name: specifies the actual field name in the records array for this column
* title: specifies the output title to show for this column
* format: empty
* align: empty
* @return void
* @access private
*/
function _determineColumnsToUse($arrRecord, &$arrColumnsToUse)
{
if ($this->_arrColumnInfo['blnUseAllColumns']) {
// Use every column available in the records array
$arrColumnsToUse = array();
foreach ($arrRecord as $strCurKey=>$val) {
$arrColInfo['name'] = $strCurKey;
$arrColInfo['title'] = $strCurKey;
$arrColInfo['format'] = '';
$arrColInfo['align'] = '';
array_push($arrColumnsToUse, $arrColInfo);
}
}
else {
// Use only the user-specified columns
$arrColumnsToUse = $this->_arrColumnInfo['arrColumnsToUse'];
}
return;
}
/**
* Initiates a report list (e.g. outputs the initial <table> tag for html format, the initial <?xml> tag for xml, etc.)
* @param string @strOutputMethod The desired output method for this row (html, tab, or csv)
* @return string The necessary header line(s) for the specified format
* @access private
*/
function _makeListHeader($strOutputMethod = 'html')
{
// Since we just started the list, our first record will be odd
$this->_blnRowIsOdd = false;
if ($strOutputMethod == 'tab' || $strOutputMethod == 'csv' || $strOutputMethod == 'qcsv') {
return('');
}
else if ($strOutputMethod == 'xml') {
return("<?xml version=\"1.0\" standalone=\"yes\"?>\r\n<NewDataSet>\r\n");
}
else {
return '<table ' . $this->_arrTitleInfo['strMainAttributes'] . ">\r\n";
}
}
/**
* Creates a "title" section (really column headers and page navigators) for the specified row of information. Essentially used
* to convert the default "html" style output method of the report into the specified output method (tab or csv).
* @param string $strRow The current row of data to encode as a title
* @param string @strOutputMethod The desired output method for this row (html, tab, or csv)
* @return string The encoded title row string
* @access private
*/
function _makeListTitles($strRow, $strOutputMethod = 'html')
{
if ($strRow == '') { return ''; }
if ($strOutputMethod == 'tab') {
$strRow = eregi_replace(' ', '', $strRow);
$strRow = eregi_replace('<br />', ' ', $strRow);
$strRow = eregi_replace('</td>', "\t", $strRow);
$strRow = eregi_replace('</th>', "\t", $strRow);
$strRow = strip_tags($strRow);
return ($strRow . "\r\n");
}
else if ($strOutputMethod == 'csv') {
$strRow = eregi_replace(' ', '', $strRow);
$strRow = eregi_replace('<br />', ' ', $strRow);
$strRow = eregi_replace('</td><td ', ',<td', $strRow);
$strRow = eregi_replace('</th><th ', ',<td', $strRow);
$strRow = eregi_replace('</td><td>', ',', $strRow);
$strRow = eregi_replace('</th><th>', ',', $strRow);
$strRow = strip_tags($strRow);
return ($strRow . "\r\n");
}
else if ($strOutputMethod == 'qcsv') {
$strRow = eregi_replace(' ', '', $strRow);
$strRow = eregi_replace('<br />', ' ', $strRow);
$strRow = eregi_replace('</td><td ', '",<td', $strRow);
$strRow = eregi_replace('</th><th ', '",<td', $strRow);
$strRow = eregi_replace('</td><td>', '","', $strRow);
$strRow = eregi_replace('</th><th>', '","', $strRow);
$strRow = strip_tags($strRow);
return ("\"$strRow\"\r\n");
}
else if ($strOutputMethod == 'xml') {
$strRow = eregi_replace('<br />', "\r\n", $strRow);
$strRow = eregi_replace('</td>', '', $strRow);
$strRow = eregi_replace('</th>', '', $strRow);
$strRow = eregi_replace('<th>', '<td>', $strRow);
$strRow = eregi_replace('<th ', '<td>', $strRow);
$strRow = eregi_replace('<td ', '<td>', $strRow);
$arrOut = array();
$arrPieces = split('<', $strRow);
for ($i = 0; $i < count($arrPieces); $i++) {
if (substr($arrPieces[$i], 0, 3) !== 'td>') {
array_push($arrOut, $arrPieces[$i]);
}
}
$strRow = join('<', $arrOut);
return ("<Table1>\r\n\t$strRow\r\n</Table1>\r\n");
}
else {
return '<tr ' . $this->_arrTitleInfo['strFieldHeadingAttributes'] . ">$strRow</tr>\r\n";
}
}
/**
* Creates a "caption" section (typically record counts) for the specified piece of information. Essentially used to
* convert the default "html" style output method of the report into the specified output method (tab or csv).
* @param string $strCaption The current piece of data to encode as a title
* @param string @strOutputMethod The desired output method for this row (html, tab, or csv)
* @return string The encoded title caption string
* @access private
*/
function _makeListCaption($strCaption, $strOutputMethod = 'html')
{
if ($strCaption == '') { return(''); }
if ($strOutputMethod == 'tab' || $strOutputMethod == 'csv' || $strOutputMethod == 'qcsv' || $strOutputMethod == 'xml') {
return($this->_makeListTitles($strCaption, $strOutputMethod));
}
else {
if ($this->_arrTitleInfo['strCaptionAttributes'] === null) {
return "<caption align=\"middle\"><font size='+1'><nobr>$strCaption</nobr></font></caption>\r\n";
}
else { return '<caption ' . $this->_arrTitleInfo['strCaptionAttributes'] . ">$strCaption</caption>\r\n"; }
}
}
/**
* Creates a data row section out of the specified row of information. Essentially used to
* convert the default "html" style output method of the report into the specified output method (tab or csv).
* @param string $strRow The current row of data to encode as a title
* @param string @strOutputMethod The desired output method for this row (html, tab, or csv)
* @return string The encoded data row string
* @access private
*/
function _makeListRow($strRow, $strOutputMethod = 'html')
{
if ($strOutputMethod == 'tab' || $strOutputMethod == 'csv' || $strOutputMethod == 'qcsv' || $strOutputMethod == 'xml') {
return($this->_makeListTitles($strRow, $strOutputMethod));
}
else {
if ($this->_blnRowIsOdd) {
$strRowAttribs = $this->_arrRowInfo['strOddRowAttributes'];
$strClassName = $this->_arrRowInfo['strOddRowClassName'];
}
else {
$strRowAttribs = $this->_arrRowInfo['strEvenRowAttributes'];
$strClassName = $this->_arrRowInfo['strEvenRowClassName'];
}
if ($this->_arrLinkInfo['blnAllowLink']) {
if ($this->_arrRowInfo['strHoverRowClassName'] != '') {
$strHoverScript = 'onmouseover="this.className=\'' . $this->_arrRowInfo['strHoverRowClassName'] . '\';" '.
'onmouseout="this.className=\'' . $strClassName . '\';"';
}
else { $strHoverScript = ''; }
$strResult = "<tr $strRowAttribs $strHoverScript>$strRow</tr>";
}
else { $strResult = "<tr $strRowAttribs>$strRow</tr>\r\n"; }
}
// Track whether we're on an even or odd row
$this->_blnRowIsOdd = !($this->_blnRowIsOdd);
return($strResult);
}
/**
* Closes a report list in the specified manner.
* @param string $strOutputMethod The desired output method for this report
* @return string The required closing value for this output method
* @access private
*/
function _makeListFooter($strOutputMethod = 'html', $blnUseHiddenColumns = false)
{
if ($strOutputMethod == 'html') {
$strFooter = "</table>\r\n";
if ($blnUseHiddenColumns) {
$strListId = $this->_strReportId;
$intControlCount = $this->_intControlId;
$intSelectorCount = ($this->_arrPagingInfo['blnShowPagingBottom'] ? 4 : 2);
$strHiddenCssClass = $this->_arrColumnInfo['strHiddenColumnClassName'];
$strFooter .= "<script type=\"text/javascript\">\n" .
"function toggleColumns_{$strListId}(do_hide)\n" .
"{\n" .
"for (i = 0; i < $intSelectorCount; i++) {\n" .
" document.getElementById('rptlist_{$strListId}_ctl_' + i).style.display = (0 == (i % 2) ? (do_hide ? 'block' : 'none') : (do_hide ? 'none' : 'block'));\n" .
"}\n" .
"for (i = 0; i < $intControlCount; i++) {\n" .
" if (do_hide) {\n" .
" rptlist_{$strListId}_classes[i] = document.getElementById('rptlist_{$strListId}_' + i).className;\n" .
" document.getElementById('rptlist_{$strListId}_' + i).className = '$strHiddenCssClass';\n" .
" }\n" .
" else {\n" .
" document.getElementById('rptlist_{$strListId}_' + i).className = rptlist_{$strListId}_classes[i];\n" .
" }\n" .
"}\n" .
"}\n" .
'var rptlist_' . $strListId . '_classes = new Array(' . $intControlCount . ");\n" .
"toggleColumns_{$strListId}(true);\n" .
"</script>\n" ;
}
return($strFooter);
}
else if ($strOutputMethod == 'xml') {
return("</NewDataSet>");
}
else { return(''); }
}
/**
* Creates the page navigation section of controls for the report. This will
* result in a series of links of the form:
* [<< Prev] | [Back] | 1 | 2 | ... | 10 | [More] | [Next >>]
* No more than 10 pages will be allowed at any time, and the current
* page will not be a hyperlink.
* @param integer $intTotalRecords The total number of records in the data
* @param integer $intNumListColumns The number of fields that are being
* displayed for the report.
*/
function _makePageNavigators($intTotalRecords, $intNumListColumns)
{
$intStartRecord = $this->_arrPagingInfo['intStartRecord'];
$intNumRecords = $this->_arrPagingInfo['intNumRecords'];
if ($intTotalRecords <= $intNumRecords || $intNumRecords <= 0) {
// All the records will fit on one page; nothing to do
return;
}
$intNumPagesNeeded = ceil($intTotalRecords / $intNumRecords);
$strRow = "<td colspan=\"$intNumListColumns\" align=\"left\">";
// What's the base for this SET of records?
$intSetBaseRecord = 0;
for ($iPage = 1; $iPage < $intNumPagesNeeded; $iPage++) {
if (($iPage * 10 * $intNumRecords) > $intStartRecord) {
// Found our page set
$intSetBaseRecord = ($iPage - 1) * 10 * $intNumRecords;
break;
}
}
if ($intNumPagesNeeded > 10) {
if ($intSetBaseRecord > 0) {
$blnNeedLessButton = true;
}
else {
$blnNeedLessButton = false;
}
if ($intSetBaseRecord < ($intTotalRecords - 10 * $intNumRecords)) {
$blnNeedMoreButton = true;
}
else {
$blnNeedMoreButton = false;
}
}
else {
$blnNeedLessButton = false;
$blnNeedMoreButton = false;
}
// DGC 8-8-2003 Handle any sort order options
if ($this->_arrSortInfo['blnAllowSort'] && $this->_arrSortInfo['strSortField'] != '') {
if ($this->_arrSortInfo['blnUsePathParams'] == 1) {
$strSortOptions = '/Sort-' . $this->_arrSortInfo['strSortField'] . '/SortDescending-' . intval($this->_arrSortInfo['blnSortDescending']);
}
else {
$strSortOptions = '&Sort=' . $this->_arrSortInfo['strSortField'] . '&SortDescending=' . intval($this->_arrSortInfo['blnSortDescending']);
}
}
else { $strSortOptions = ''; }
// DGC 3-28-2004 While we're at it, if there's an HREF anchor target specified, grab that here
if ($this->_arrPagingInfo['strPagingAnchor'] != '') {
$strSortOptions .= '#' . $this->_arrPagingInfo['strPagingAnchor'];
}
// Show the prior page and prior set navigators
if ($intStartRecord > 0) {
// Not showing the first page; show a link to the prior page
$intBase = $intStartRecord - $intNumRecords;
$strLink = '<a href="' . $this->_arrPagingInfo['strPagingURL'] . $intBase . $strSortOptions . '">';
$strRow .= "$strLink<< Prev</a> | ";
if ($blnNeedLessButton) {
// Show the Back option to move to a different set of pages
$intBase = $intSetBaseRecord - (10 * $intNumRecords);
$strLink = '<a href="' . $this->_arrPagingInfo['strPagingURL'] . $intBase . $strSortOptions . '">';
$strRow .= $strLink . 'Back</a> | ';
}
else {
$strRow .= 'Back | ';
}
}
else {
$strRow .= '<< Prev | ';
$strRow .= 'Back | ';
}
// Show the regular page count navigators
for ($iPage = 0; $iPage < $intNumPagesNeeded; $iPage++) {
if (($iPage * $intNumRecords) >= ($intSetBaseRecord + 10 * $intNumRecords)){
break;
}
if (($iPage * $intNumRecords) >= $intSetBaseRecord) {
if (($iPage * $intNumRecords) == $intStartRecord) {
// Found the current page
$strRow .= ($iPage + 1 ) . ' | ';
}
else {
$intBase = $iPage * $intNumRecords;
$strLink = '<a href="' . $this->_arrPagingInfo['strPagingURL'] . $intBase . $strSortOptions . '">';
$strRow .= $strLink . ($iPage + 1) . '</a> | ';
}
}
}
// Show the next page and next set navigators
if ($intStartRecord < ($intTotalRecords - $intNumRecords)) {
if ($blnNeedMoreButton) {
// Show the Back option to move to a different set of pages
$intBase = $intSetBaseRecord + 10 * $intNumRecords;
$strLink = '<a href="' . $this->_arrPagingInfo['strPagingURL'] . $intBase . $strSortOptions . '">';
$strRow .= $strLink . 'More</a> | ';
}
else {
$strRow .= 'More | ';
}
// Not showing the first page; show a link to the prior page
$intBase = $intStartRecord + $intNumRecords;
$strLink = '<a href="' . $this->_arrPagingInfo['strPagingURL'] . $intBase . $strSortOptions . '">';
$strRow .= $strLink . 'Next >></a>';
}
else {
$strRow .= 'More | ';
$strRow .= 'Next >>';
}
$strResult = $this->_makeListTitles($strRow . '</td>');
$strResult = str_replace('<tr ' . $this->_arrTitleInfo['strFieldHeadingAttributes'] . '>', '<tr ' . $this->_arrTitleInfo['strPageNavigatorAttributes'] . '>', $strResult);
return($strResult);
}
/**
* Used to construct the HREF link for each data value in reports that are configured with linking
* @param array $arrLinkInfo The array of information about the link URL to construct (whether it
* be for an entire row of data, or a custom link for a given field
* @param array $arrRecord The array of data for the current record (any field of which can be
* used in constructing the link
* @param boolean $blnCloseQuotes True/False for whether or not to close the quotes of the link,
* useful in case additional parameters must be added to the link by some other method
* @return string
* @access private
*/
function _buildLinkURLFromRecord($arrLinkInfo, $arrRecord, $blnCloseQuotes = true)
{
// If our link is conditional, see if this record meets the necessary requirements
if ($arrLinkInfo['strLinkCondition'] != '') {
$strTest = $arrLinkInfo['strLinkCondition'];
if (strpos($strTest, '%%') !== false) {
foreach ($arrRecord as $key=>$val) {
$strTest = str_replace('%%' . $key . '%%', $val, $strTest);
}
}
if (!(eval('return(' . $strTest . ');'))) {
// False test condition; don't show a link
return('');
}
}
$strURL = '"' . $arrLinkInfo['strLinkURL'];
// If we're employing JavaScript pop-ups, handle that here
if ($arrLinkInfo['strLinkEvents'] != '') {
$strURL .= '" ' . $arrLinkInfo['strLinkEvents'];
}
// See if we need to do dynamic value replacement into the LinkURL target, i.e.
// does our link target have variables in the base such as "%%strPageName%%.php"
if (strpos($strURL, '%%') !== false) {
foreach ($arrRecord as $key=>$val) {
$strURL = str_replace('%%' . $key . '%%', $val, $strURL);
}
}
$intNumParams = count($arrLinkInfo['arrLinkParamFields']);
if ($intNumParams == 0) {
if ($blnCloseQuotes) {
return($strURL . '"');
}
else { return($strURL); }
}
// Check to see if a ''?'' is already in the URL, in which case
// we should start using the ''&'' right away
if (strpos($strURL, '?')) {
$strConcat = '&';
}
else { $strConcat = '?'; }
$arrLinkParamFields = $arrLinkInfo['arrLinkParamFields'];
for ($iParam = 0; $iParam < $intNumParams; $iParam++) {
$strCurParamMarker = $arrLinkParamFields[$iParam]['marker'];
$strCurParamName = $arrLinkParamFields[$iParam]['name'];
$strCurParamValue = $arrRecord[$strCurParamName];
// POSSIBLE TODO: make the param name and value safe for including in a URL
$strURL .= "$strConcat$strCurParamMarker=$strCurParamValue";
$strConcat = '&';
}
if ($arrLinkInfo['strLinkAnchor'] != '') { $strURL .= '#' . $arrLinkInfo['strLinkAnchor']; }
if ($blnCloseQuotes) { $strURL .= '"'; }
return($strURL);
}
/**
* Gets the section "key" (unique identifier) for the specified record, i.e. what section
* of the report does this record belong in. Used for multi-line lists.
* @param array $curRecord The current record to examine for section key information
* @return string The section "key" for this record
* @access private
*/
function _getSectionKeyValue($curRecord)
{
$strValue = '';
$arrKeys = $this->_arrColumnInfo['arrSectionKeys'];
foreach ($arrKeys as $keyField) {
if ($strValue == '') {
$strValue .= $curRecord[$keyField];
}
else { $strValue .= ', ' . $curRecord[$keyField]; }
}
return $strValue;
}
/**
* Constructs an XML-safe node name to use, from a raw field title, e.g.
* "Product Name" would become "Product_Name".
* @param string $strFieldName The name value to sanitize
* @return string The cleansed/XML-safe string to use as a node name
* @access private
*/
function _getXmlNodeName($strFieldName)
{
$strFieldName = trim($strFieldName);
if ($strFieldName == '') { return(''); }
$strFieldName = htmlentities($strFieldName);
$strFieldName = str_replace('#', 'Number', $strFieldName);
$strFieldName = preg_replace('/[^a-zA-Z0-9_]/', '_', $strFieldName);
while (strpos($strFieldName, '__') !== false) {
$strFieldName = str_replace('__', '_', $strFieldName);
}
// First character must be a letter, underscore
if (preg_match('/^[a-zA-Z_]/', $strFieldName) >= 1) {
// Good; should be safe to use as is
}
else {
// Modify the name to make it usable; pre-pend an underscore
$strFieldName = '_' . $strFieldName;
}
return($strFieldName);
}
/**
* Formats a given string value for display, to the specified format type. Currently
* supported formats are:
* phone - takes unformatted digits and uses parentheses and dashes,
* for example: "8016060801" -> "(801) 606-0801"
* ucase/lcase/pcase
* decimal/money - commas every 3 digits, and 2 decimal places
* dollars - uses American dollar signs in front of money values (ex: $3.54 or ($546.78) for
* negative values).
* bool - True/False
* yesno - Yes/No
* wrapped - beaks lines at 100 characters using <br> tags
* date - mm/dd/yyyy
* datetime - mm/dd/yyyy HH:MM
* shortdate - mm/dd/yy
* shortdatetime - mm/dd/yy HH:MM
* int/integer
* Date-related formats suffer the same limitations as many date manipulations
* in Linux, i.e. dates prior to the Unix date epoch are often problematic.
* @param string $strValue The current value to format
* @param string $strFormat The format type to conform to (phone, ucase, etc.)
* @return string The formatted value for display
* @access private
*/
function _formatListValue($strValue, $strFormat)
{
switch ($strFormat) {
case 'phone' :
$strValue = trim($strValue);
switch (strlen($strValue)) {
case 7 :
// 6060801 -> 606-0801
$strValue = substr($strValue, 0, 3) . '-' .
substr($strValue, 3);
break;
case 10 :
// 8016060801 -> (801) 606-0801
$strValue = '(' . substr($strValue, 0, 3) . ') ' .
substr($strValue, 3, 3) . '-' .
substr($strValue, 6);
break;
default :
break;
}
break;
case 'ucase' :
$strValue = strtoupper($strValue);
break;
case 'lcase' :
$strValue = strtolower($strValue);
break;
case 'pcase' :
$strValue = ucwords(strtolower($strValue));
break;
case 'bool' :
if ($strValue) { $strValue = 'True'; }
else { $strValue = 'False'; }
break;
case 'yesno' :
if ($strValue) { $strValue = 'Yes'; }
else { $strValue = 'No'; }
break;
case 'money' :
$strValue = number_format($strValue, 2);
break;
case 'dollars' :
$dblValue = doubleval($strValue);
if ($dblValue < 0) {
$strValue = number_format(-1 * $dblValue, 2);
$strValue = '($' . $strValue . ')';
}
else {
$strValue = number_format($dblValue, 2);
$strValue = '$' . $strValue;
}
break;
case 'decimal' :
$strValue = number_format($strValue, 2);
break;
case 'wrapped' :
$strValue = wordwrap($strValue, 100, '<br />');
break;
case 'date' :
case 'shortdate' :
if ($strValue == '') {
$strValue = '';
}
else {
if (strlen($strValue) < 6) {
$strValue = '';
}
else {
$varDate = getDate(strtotime($strValue));
if ($strFormat == 'date') {
$strValue = sprintf('%\'02d', $varDate['mon']) . '/' .
sprintf('%\'02d', $varDate['mday']) . '/' .
sprintf('%\'04d', $varDate['year']);
}
else {
$strValue = sprintf('%\'02d', $varDate['mon']) . '/' .
sprintf('%\'02d', $varDate['mday']) . '/' .
sprintf('%\'02d', substr($varDate['year'], -2));
}
}
}
break;
case 'datetime' :
case 'shortdatetime' :
if ($strValue == '') {
$strValue = '';
}
else {
if (strlen($strValue) < 6) {
$strValue = '';
}
else {
$varDate = getDate(strtotime($strValue));
if ($strFormat == 'datetime') {
$strValue = sprintf('%\'02d', $varDate['mon']) . '/' .
sprintf('%\'02d', $varDate['mday']) . '/' .
sprintf('%\'04d', $varDate['year']) . ' ' .
sprintf('%\'02d', $varDate['hours']) . ':' .
sprintf('%\'02d', $varDate['minutes']);
}
else {
$strValue = sprintf('%\'02d', $varDate['mon']) . '/' .
sprintf('%\'02d', $varDate['mday']) . '/' .
sprintf('%\'02d', substr($varDate['year'], -2)) . ' ' .
sprintf('%\'02d', $varDate['hours']) . ':' .
sprintf('%\'02d', $varDate['minutes']);
}
}
}
break;
case 'int':
case 'integer':
// Show integer values
$strValue = intval($strValue);
break;
case 'longint':
// Show integer values but with comma separators
if (is_numeric($strValue)) {
$strValue = number_format($strValue, 0);
}
break;
case 'percent':
case 'shortpercent':
case 'merchantlistpercent':
if ($strFormat == 'percent') {
// Hack for flat fee commissions in merchant list
if (strpos($strValue, 'flatfee') !== false) {
$arrFlatFee = explode('-', $strValue);
$dblValue = doubleval($arrFlatFee[1]);
$strValue = number_format($dblValue, 2);
$strValue = '$' . $strValue;
}
else { $strValue = number_format(doubleval($strValue), 2) . '%'; }
}
elseif ($strFormat == 'shortpercent') {
$strValue = round($strValue) . '%';
}
else {
if ($strValue != 'New Program') {
$strValue = number_format(doubleval($strValue), 2) . '%';
}
}
break;
case 'image':
if ($strValue != '') {
$strValue = '<img src="' . $strValue . '" border=0 />';
}
default :
break;
}
return $strValue;
}
}
?>