* */ require_once('db_filter.php'); define('FILTER_VALUES_NORMAL', 1); define('FILTER_VALUES_ENUM', 2); define('FILTER_VALUES_OPTION_BOOL', 3); define('FILTER_VALUES_OPTION_ENUM', 4); define('MAX_FILTERS', 50); /* Info describing an available filter: what column it applies to, and what comparison options are available */ class FilterInfo { private $sColumn; private $sDisplayName; private $aTypes; // Available filters for this column private $iValueType; // Normal, enum ... private $aValueTypeData; // List of enums private $aValueTypeDataDisplay; // Optional display names for enums public function FilterInfo($sColumn, $sDisplayName, $aTypes, $iValueType = FILTER_VALUES_NORMAL, $aValueTypeData = array(), $aValueTypeDisplay = array()) { $this->sColumn = $sColumn; $this->sDisplayName = $sDisplayName; $this->aTypes = $aTypes; $this->iValueType = $iValueType; $this->aValueTypeData = $aValueTypeData; if(sizeof($aValueTypeData) && !sizeof($aValueTypeDisplay)) $this->aValueTypeDataDisplay = $aValueTypeData; else $this->aValueTypeDataDisplay = $aValueTypeDisplay; } public function getColumn() { return $this->sColumn; } public function getDisplayName() { return $this->sDisplayName; } public function getValueType() { return $this->iValueType; } public function getValueTypeData() { return $this->aValueTypeData; } public function getValueTypeDataDisplay() { return $this->aValueTypeDataDisplay; } public function getTypes() { return $this->aTypes; } public function isOption() { switch($this->iValueType) { case FILTER_VALUES_OPTION_BOOL: case FILTER_VALUES_OPTION_ENUM: return true; default: return false; } } public static function getOpName($iOpId) { switch($iOpId) { case FILTER_EQUALS: return 'equal to'; case FILTER_LIKE: return 'like'; case FILTER_CONTAINS: return 'contains'; case FILTER_STARTS_WITH: return 'starts with'; case FILTER_ENDS_WITH: return 'ends with'; case FILTER_NOT_LIKE: return 'not like'; case FILTER_NOT_EQUALS: return 'not equal to'; case FILTER_LESS_THAN: return 'less than'; case FILTER_GREATER_THAN: return 'greater than'; } } } /* Class handling tables where the user can filter contents */ class FilterInterface { private $aFilterInfo; private $oFilterSet; private $aEscapeChars; private $aEscapeCharsWith; private $sErrors; // Used to inform the user about errors (and to give advice) public function FilterInterface($sTableName = '') { $this->aFilterInfo = array(); $this->oFilterSet = new FilterSet(query_escape_string($sTableName)); $this->aEscapeChars = array('.'); $this->aEscapeCharsWith = array('-'); $this->sErrors = ''; } public function AddFilterObject(Filter $oFilter) { $this->oFilterSet->AddFilterObject($oFilter); } public function setFilterSet(FilterSet $oSet) { $this->oFilterSet = $oSet; } /* Convenience function to add a filter option */ public function AddFilterInfo($sColumn, $sDisplayName, $aTypes, $iValueType = VALUE_TYPE_NORMAL, $aValueTypeData = array(), $aValueTypeDisplay = array()) { $this->aFilterInfo[$sColumn] = new FilterInfo($sColumn, $sDisplayName, $aTypes, $iValueType, $aValueTypeData, $aValueTypeDisplay); } /* We can't use some special chars in variable names, such as '.' */ public function escapeChars($sIn) { return str_replace($this->aEscapeChars, $this->aEscapeCharsWith, $sIn); } public function unescapeChars($sIn) { return str_replace($this->aEscapeWith, $this->aEscape, $sIn); } public function getUrlElement($iId, Filter $oFilter) { $sColumn = $this->escapeChars($oFilter->getColumn()); $sId = $iId; $shEditor = "&i{$sColumn}Op$sId={$oFilter->getOperatorId()}"; $shEditor .= "&s{$sColumn}Data$sId={$oFilter->getData()}"; return $shEditor; } public function getHiddenInputTag($iId, Filter $oFilter) { $sColumn = $this->escapeChars($oFilter->getColumn()); $sId = $iId; $shEditor = "getOperatorId()}\">"; $shEditor .= "getData()}\" />"; return $shEditor; } public function getOptionBoolEditor($iId, Filter $oFilter) { $sColumn = $this->escapeChars($oFilter->getColumn()); $oColumn = $this->aFilterInfo[$oFilter->getColumn()]; $sId = ($iId == -1) ? '' : $iId; $aTypes = $oColumn->getTypes(); $iOp = $aTypes[0]; if($iId == -1) { /* The first entry in the list of choices is the default */ $aValues = $oColumn->getValueTypeData(); $sData = $aValues[0]; } else { $sData = $oFilter->getData(); } $shRet = ""; if($sData == 'true') $sChecked = ' checked="checked"'; else $sChecked = ''; $shRet .= ""; $shRet .= ' '.$oColumn->getDisplayName(); return $shRet; } public function getItemEditor($iId, Filter $oFilter) { $sColumn = $this->escapeChars($oFilter->getColumn()); $oColumn = $this->aFilterInfo[$oFilter->getColumn()]; $sId = ($iId == -1) ? '' : $iId; $shEditor = $oColumn->getDisplayName().' '; $aTypes = $oColumn->getTypes(); /* It doesn't make sense to show a dropdown menu of choices if there is only one If the filter is already active then there are more than one; one to remove */ if($iId == -1 && sizeof($aTypes) == 1) { echo ""; /* Printing 'equal to' sounds weird if it is the only choice */ if($aTypes[0] != FILTER_EQUALS) $shEditor .= $oColumn->getOpName($aTypes[0]); } else if ($aTypes[0] != FILTER_OPTION_ENUM) { $shEditor .= " '; } else { echo ""; } switch($oColumn->getValueType()) { case FILTER_VALUES_NORMAL: $shEditor .= "getData()}\" name='s{$sColumn}Data$sId' size='30' />"; break; case FILTER_VALUES_ENUM: case FILTER_VALUES_OPTION_ENUM: $shEditor .= $this->getEnumEditor($oColumn, $oFilter, $sId); break; } return $shEditor; } public function getEnumEditor($oColumn, $oFilter, $sId) { $sColumn = $this->escapeChars($oFilter->getColumn()); $aOptions = $oColumn->getValueTypeData(); $aOptionNames = $oColumn->getValueTypeDataDisplay(); $sData = $oFilter->getData(); $shEditor = ""; return $shEditor; } /* Get filter data formatted to fit in a URL */ public function getUrlData() { $shEditor = ''; $aCounts = array(); foreach($this->oFilterSet->getFilters() as $oFilter) { $sColumn = $oFilter->getColumn(); if(!array_key_exists($sColumn, $aCounts)) $aCounts[$sColumn] = 0; $shEditor .= $this->getUrlElement($aCounts[$sColumn], $oFilter); $shEditor .= '
'; $aCounts[$sColumn]++; } return $shEditor; } /* Get a list of hidden input tags to preserve form data */ public function getHiddenFormData() { $shEditor = ''; $aCounts = array(); foreach($this->oFilterSet->getFilters() as $oFilter) { $sColumn = $oFilter->getColumn(); if(!array_key_exists($sColumn, $aCounts)) $aCounts[$sColumn] = 0; $shEditor .= $this->getHiddenInputTag($aCounts[$sColumn], $oFilter); $shEditor .= '
'; $aCounts[$sColumn]++; } return $shEditor; } public function getEditor() { $shNewItemsEditor = ''; $shCurrentItemsEditor = ''; $aCounts = array(); if(sizeof($this->oFilterSet->getFilters())) $shCurrentItemsEditor .= '
Active filters
'; foreach($this->oFilterSet->getFilters() as $oFilter) { $sColumn = $oFilter->getColumn(); if(!array_key_exists($sColumn, $aCounts)) $aCounts[$sColumn] = 0; if($oFilter->getOperatorId() == FILTER_OPTION_BOOL) $shCurrentItemsEditor .= $this->getOptionBoolEditor($aCounts[$sColumn], $oFilter); else $shCurrentItemsEditor .= $this->getItemEditor($aCounts[$sColumn], $oFilter); $shCurrentItemsEditor .= '
'; $aCounts[$sColumn]++; } $shNewItemsEditor .= 'Add new filter (You don’t have to fill out all rows.)
'; /* Show errors, if any */ if($this->sErrors) $shNewItemsEditor .= "{$this->sErrors}"; foreach($this->aFilterInfo as $oOption) { $oDummyFilter = new Filter($oOption->getColumn(), 0, ''); $aTypes = $oOption->getTypes(); if($oOption->getValueType() == FILTER_VALUES_OPTION_BOOL) { if(!array_key_exists($oOption->getColumn(), $aCounts)) $shNewItemsEditor .= $this->getOptionBoolEditor(-1, $oDummyFilter); $shNewItemsEditor .= '
'; } else { /* Make necessary checks for filters that are only supposed to be shown once */ if($oOption->getValueType() != FILTER_VALUES_OPTION_ENUM || !array_key_exists($oOption->getColumn(), $aCounts)) { $shNewItemsEditor .= $this->getItemEditor(-1, $oDummyFilter); $shNewItemsEditor .= '
'; } } } return $shNewItemsEditor.$shCurrentItemsEditor; } public function getFilterInfo() { return $this->aFilterInfo; } /* Reads all input related to filters for the given table column */ public function readInputForColumn($aClean, FilterInfo $oOption) { $aReturn = array(); $bChangedOption = false; for($i = 0; array_key_exists('i'.$this->escapeChars($oOption->getColumn())."Op$i", $aClean); $i++) { $sColumn = $this->escapeChars($oOption->getColumn()); $sData = query_escape_string(getInput("s{$sColumn}Data$i", $aClean)); $iOp = $aClean["i{$sColumn}Op$i"]; if(!$iOp) continue; $oFilter = new Filter($oOption->getColumn(), $iOp, $sData); /* Only show an option as an active filter if it has been changed from the default */ if($oOption->getValueType() == FILTER_VALUES_OPTION_BOOL || $oOption->getValueType() == FILTER_VALUES_OPTION_ENUM) { if($oOption->getValueType() == FILTER_VALUES_OPTION_BOOL) { /* The default option is the first entry in the list of choices */ $aChoices = $oOption->getValueTypeData(); $sDefault = $aChoices[0]; if(!$sData) $sData = 'false'; if($sData == $sDefault) continue; } if($i > 0) continue; $bChangedOption = true; } if(!$sData) continue; $aReturn[] = $oFilter; } if(array_key_exists('i'.$this->escapeChars($oOption->getColumn())."Op", $aClean)) { $sColumn = $this->escapeChars($oOption->getColumn()); $i = sizeof($aReturn); $sData = query_escape_string($aClean["s{$sColumn}Data"]); $iOp = $aClean["i{$sColumn}Op"]; if($iOp && $sData && ($oOption->getValueType() != FILTER_VALUES_OPTION_BOOL || !$bChangedOoption)) { $oFilter = new Filter($oOption->getColumn(), $iOp, $sData); $aReturn[] = $oFilter; } else if(!$iOp && $sData) { /* The user probably meant to add a filter, but forgot to seelect a filter criterion */ $this->sErrors .= 'You need to select a filter criterion from the drop-down list
'; } } return $aReturn; } /* Reads an input array get enabled filters from form data. The given TableFilterSet defines available options */ public function readInput($aClean) { $iCount = 0; // We set a maximum for how many filters a user can add, // otherwise we may get a too long SQL query foreach($this->getFilterInfo() as $oOption) { foreach($this->readInputForColumn($aClean, $oOption) as $oNewFilter) { $iCount ++; $this->AddFilterObject($oNewFilter); if($iCount > MAX_FILTERS) break; } } } public function loadTable($sTableName) { $this->oFilterSet->loadTable($sTableName); } public function saveTable($sTableName) { $this->oFilterSet->saveTable($sTableName); } public function getFilterCount() { return $this->oFilterSet->getFilterCount(); } /* Returns an array of options, where the keys are the columns and the members are the settings themselves */ public function getOptions() { $aOptions = array(); foreach($this->oFilterSet->getFilters() as $oFilter) { if($oFilter->isOption()) $aOptions[$oFilter->getColumn()] = $oFilter->getData(); } foreach($this->aFilterInfo as $oFilterInfo) { if($oFilterInfo->isOption() && !array_key_exists($oFilterInfo->getColumn(), $aOptions)) { $aTypes = $oFilterInfo->getTypes(); if($oFilterInfo->getValueType() == FILTER_VALUES_OPTION_BOOL) $sDefault = $aTypes[0]; else $sDefault = ''; $aOptions[$oFilterInfo->getColumn()] = $sDefault; } } return $aOptions; } public function getWhereClause() { return $this->oFilterSet->getWhereClause(); } public function getTable($sTable, $iLimit = 0) { $hResult = $this->oFilterSet->getMatchedItems($sTable, $iLimit); if(!$hResult) return; echo 'Selected '.$this->oFilterSet->getMatchedItemsCount($sTable).' rows

'; $oTable = new Table(); while($aRow = mysql_fetch_row($hResult)) { $oRow = new TableRow(); foreach($aRow as $sCell) { $oRow->AddTextCell($sCell); } $oTable->AddRow($oRow); } return $oTable->getString(); } } ?>