Integrate filtering system with objectManager, use it for applications

This commit is contained in:
Alexander Nicolaysen Sørnes
2008-04-14 01:26:37 +02:00
committed by Chris Morgan
parent b2adeab95a
commit 4f792a28ba
5 changed files with 266 additions and 38 deletions

View File

@@ -11,6 +11,7 @@ require_once(BASE."include/util.php");
require_once(BASE."include/mail.php"); require_once(BASE."include/mail.php");
require_once(BASE."include/maintainer.php"); require_once(BASE."include/maintainer.php");
require_once(BASE."include/tableve.php"); require_once(BASE."include/tableve.php");
require_once(BASE."include/db_filter_ui.php");
define("PLATINUM_RATING", "Platinum"); define("PLATINUM_RATING", "Platinum");
define("GOLD_RATING", "Gold"); define("GOLD_RATING", "Gold");
@@ -939,11 +940,18 @@ class Application {
return 'appId'; return 'appId';
} }
public static function objectGetEntries($sState, $iRows = 0, $iStart = 0, $sOrderBy = "appId", $bAscending = TRUE) public static function objectGetEntries($sState, $iRows = 0, $iStart = 0, $sOrderBy = "appId", $bAscending = TRUE, $oFilters = null)
{ {
$sLimit = ""; $sLimit = "";
$sOrdering = $bAscending ? "ASC" : "DESC"; $sOrdering = $bAscending ? "ASC" : "DESC";
$sExtraTables = '';
$sWhereFilter = $oFilters ? $oFilters->getWhereClause() : '';
if($sWhereFilter)
{
$sExtraTables = ',appVersion';
$sWhereFilter = " AND appVersion.appId = appFamily.appId AND $sWhereFilter";
}
/* Should we add a limit clause to the query? */ /* Should we add a limit clause to the query? */
if($iRows || $iStart) if($iRows || $iStart)
{ {
@@ -955,10 +963,10 @@ class Application {
$iRows = application::objectGetEntriesCount($sState); $iRows = application::objectGetEntriesCount($sState);
} }
$sQuery = "SELECT appFamily.*, vendor.vendorName AS vendorName FROM appFamily, vendor WHERE $sQuery = "SELECT DISTINCT(appFamily.appId), appFamily.*, vendor.vendorName AS vendorName FROM appFamily, vendor$sExtraTables WHERE
appFamily.vendorId = vendor.vendorId appFamily.vendorId = vendor.vendorId
AND AND
appFamily.state = '?'"; appFamily.state = '?'$sWhereFilter";
if($sState != 'accepted' && !application::canEdit()) if($sState != 'accepted' && !application::canEdit())
{ {
@@ -997,6 +1005,14 @@ class Application {
return $hResult; return $hResult;
} }
public static function objectGetFilterInfo()
{
$oFilter = new FilterInterface();
$oFilter->AddFilterInfo('appVersion.rating', 'Rating', array(FILTER_EQUALS, FILTER_LESS_THAN, FILTER_GREATER_THAN), FILTER_VALUES_ENUM, array('Platinum', 'Gold', 'Silver', 'Bronze', 'Garbage'));
return $oFilter;
}
public static function objectGetSortableFields() public static function objectGetSortableFields()
{ {
return array('submitTime', 'appName', 'appId', 'userName', 'vendorName'); return array('submitTime', 'appName', 'appId', 'userName', 'vendorName');
@@ -1112,23 +1128,31 @@ class Application {
} }
} }
public static function objectGetEntriesCount($sState) public static function objectGetEntriesCount($sState, $oFilters = null)
{ {
$sExtraTables = '';
$sWhereFilter = $oFilters ? $oFilters->getWhereClause() : '';
if($sWhereFilter)
{
$sExtraTables = ',appVersion';
$sWhereFilter = " AND appVersion.appId = appFamily.appId AND $sWhereFilter";
}
if($sState != 'accepted' && !application::canEdit()) if($sState != 'accepted' && !application::canEdit())
{ {
/* Without edit rights users can only resubmit their rejected entries */ /* Without edit rights users can only resubmit their rejected entries */
if(!$bRejected) if(!$bRejected)
return FALSE; return FALSE;
$sQuery = "SELECT COUNT(appId) as count FROM appFamily WHERE $sQuery = "SELECT COUNT(DISTINCT(appFamily.appId)) as count FROM appFamily$sExtraTables WHERE
submitterId = '?' submitterId = '?'
AND AND
state = '?'"; appFamily.state = '?'$sWhereFilter";
$hResult = query_parameters($sQuery, $_SESSION['current']->iUserId, $hResult = query_parameters($sQuery, $_SESSION['current']->iUserId,
$sState); $sState);
} else } else
{ {
$sQuery = "SELECT COUNT(appId) as count FROM appFamily WHERE state = '?'"; $sQuery = "SELECT COUNT(DISTINCT(appFamily.appId)) as count FROM appFamily$sExtraTables WHERE appFamily.state = '?'$sWhereFilter";
$hResult = query_parameters($sQuery, $sState); $hResult = query_parameters($sQuery, $sState);
} }

View File

@@ -14,6 +14,7 @@ define('FILTER_GREATER_THAN', 3);
define('FILTER_LESS_THAN', 4); define('FILTER_LESS_THAN', 4);
define('FILTER_NOT_EQUALS', 5); define('FILTER_NOT_EQUALS', 5);
define('FILTER_NOT_LIKE', 6); define('FILTER_NOT_LIKE', 6);
define('FILTER_ENUM', 7);
/* A filter as part of an SQL query, such as something = 'somevalue' */ /* A filter as part of an SQL query, such as something = 'somevalue' */
class Filter class Filter

View File

@@ -10,6 +10,10 @@
require_once('db_filter.php'); require_once('db_filter.php');
define(FILTER_VALUES_NORMAL, 1);
define(FILTER_VALUES_ENUM, 2);
define(FILTER_VALUES_BOOL, 3);
/* Info describing an available filter: what column it applies to, /* Info describing an available filter: what column it applies to,
and what comparison options are available */ and what comparison options are available */
class FilterInfo class FilterInfo
@@ -17,12 +21,22 @@ class FilterInfo
private $sColumn; private $sColumn;
private $sDisplayName; private $sDisplayName;
private $aTypes; // Available filters for this column 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) public function FilterInfo($sColumn, $sDisplayName, $aTypes, $iValueType = FILTER_VALUES_NORMAL, $aValueTypeData = array(), $aValueTypeDisplay = array())
{ {
$this->sColumn = $sColumn; $this->sColumn = $sColumn;
$this->sDisplayName = $sDisplayName; $this->sDisplayName = $sDisplayName;
$this->aTypes = $aTypes; $this->aTypes = $aTypes;
$this->iValueType = $iValueType;
$this->aValueTypeData = $aValueTypeData;
if(sizeof($aValueTypeData) && !sizeof($aValueTypeDisplay))
$this->aValueTypeDataDisplay = $aValueTypeData;
else
$this->aValueTypeDataDisplay = $aValueTypeDisplay;
} }
public function getColumn() public function getColumn()
@@ -35,6 +49,21 @@ class FilterInfo
return $this->sDisplayName; return $this->sDisplayName;
} }
public function getValueType()
{
return $this->iValueType;
}
public function getValueTypeData()
{
return $this->aValueTypeData;
}
public function getValueTypeDataDisplay()
{
return $this->aValueTypeDataDisplay;
}
public function getTypes() public function getTypes()
{ {
return $this->aTypes; return $this->aTypes;
@@ -65,11 +94,15 @@ class FilterInterface
{ {
private $aFilterInfo; private $aFilterInfo;
private $oFilterSet; private $oFilterSet;
private $aEscapeChars;
private $aEscapeCharsWith;
public function FilterInterface($sTableName = '') public function FilterInterface($sTableName = '')
{ {
$this->aFilterInfo = array(); $this->aFilterInfo = array();
$this->oFilterSet = new FilterSet(mysql_real_escape_string($sTableName)); $this->oFilterSet = new FilterSet(query_escape_string($sTableName));
$this->aEscapeChars = array('.');
$this->aEscapeCharsWith = array('-');
} }
public function AddFilterObject(Filter $oFilter) public function AddFilterObject(Filter $oFilter)
@@ -83,15 +116,53 @@ class FilterInterface
} }
/* Convenience function to add a filter option */ /* Convenience function to add a filter option */
public function AddFilterInfo($sColumn, $sDisplayName, $aTypes) public function AddFilterInfo($sColumn, $sDisplayName, $aTypes, $iValueType = VALUE_TYPE_NORMAL, $aValueTypeData = array(), $aValueTypeDisplay = array())
{ {
$this->aFilterInfo[$sColumn] = new FilterInfo($sColumn, $sDisplayName, $aTypes); $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());
$oColumn = $this->aFilterInfo[$sColumn];
$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());
$oColumn = $this->aFilterInfo[$sColumn];
$sId = $iId;
$shEditor = "<input type=\"hidden\" name=\"i{$sColumn}Op$sId\" value=\"{$oFilter->getOperatorId()}\">";
$shEditor .= "<input type=\"hidden\" name=\"s{$sColumn}Data$sId\" value=\"{$oFilter->getData()}\" />";
return $shEditor;
}
public function getItemEditor($iId, Filter $oFilter) public function getItemEditor($iId, Filter $oFilter)
{ {
$sColumn = $oFilter->getColumn(); $sColumn = $this->escapeChars($oFilter->getColumn());
$oColumn = $this->aFilterInfo[$sColumn]; $oColumn = $this->aFilterInfo[$oFilter->getColumn()];
$sId = ($iId == -1) ? '' : $iId; $sId = ($iId == -1) ? '' : $iId;
$shEditor = $oColumn->getDisplayName(); $shEditor = $oColumn->getDisplayName();
@@ -121,7 +192,90 @@ class FilterInterface
$shEditor .= '</select> '; $shEditor .= '</select> ';
$shEditor .= "<input type='text' value=\"{$oFilter->getData()}\" name='s{$sColumn}Data$sId' size='30' />"; switch($oColumn->getValueType())
{
case FILTER_VALUES_NORMAL:
$shEditor .= "<input type='text' value=\"{$oFilter->getData()}\" name='s{$sColumn}Data$sId' size='30' />";
break;
case FILTER_VALUES_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 .= "<select name=\"s{$sColumn}Data$sId\">";
if($sData)
$shEditor .= "<option value=\"\">-- remove --</option>";
else
$shEditor .= "<option value=\"\">-- select --</option>";
for($i = 0; $i < sizeof($aOptions); $i++)
{
$sOption = $aOptions[$i];
$sSelected = '';
if($sData == $sOption)
$sSelected = ' selected="selected"';
$shEditor .= "<option value=\"$sOption\"$sSelected>{$aOptionNames[$i]}</option>";
}
$shEditor .= "</select>";
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 .= '<br />';
$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 .= '<br />';
$aCounts[$sColumn]++;
}
return $shEditor; return $shEditor;
} }
@@ -131,16 +285,18 @@ class FilterInterface
$shEditor = ''; $shEditor = '';
$aCounts = array(); $aCounts = array();
$shEditor .= 'Add new filter<br />'; $shEditor .= '<b>Add new filter</b> <i>(You don&#8217;t have to fill out all rows.)</i><br />';
foreach($this->aFilterInfo as $oOption) foreach($this->aFilterInfo as $oOption)
{ {
$oDummyFilter = new Filter($oOption->getColumn(), 0, ''); $oDummyFilter = new Filter($oOption->getColumn(), 0, '');
$shEditor .= $this->getItemEditor(-1, $oDummyFilter); $aTypes = $oOption->getTypes();
$shEditor .= $this->getItemEditor(-1, $oDummyFilter);
$shEditor .= '<br />'; $shEditor .= '<br />';
} }
if(sizeof($this->oFilterSet->getFilters())) if(sizeof($this->oFilterSet->getFilters()))
$shEditor .= '<br />Active filters<br />'; $shEditor .= '<br /><b>Active filters</b><br />';
foreach($this->oFilterSet->getFilters() as $oFilter) foreach($this->oFilterSet->getFilters() as $oFilter)
{ {
$sColumn = $oFilter->getColumn(); $sColumn = $oFilter->getColumn();
@@ -167,23 +323,26 @@ class FilterInterface
{ {
$aReturn = array(); $aReturn = array();
for($i = 0; array_key_exists('i'.$oOption->getColumn()."Op$i", $aClean); $i++) for($i = 0; array_key_exists('i'.$this->escapeChars($oOption->getColumn())."Op$i", $aClean); $i++)
{ {
$sData = mysql_real_escape_string($aClean["s{$oOption->getColumn()}Data$i"]); $sColumn = $this->escapeChars($oOption->getColumn());
$iOp = $aClean["i{$oOption->getColumn()}Op$i"]; $sData = query_escape_string($aClean["s{$sColumn}Data$i"]);
$iOp = $aClean["i{$sColumn}Op$i"];
if(!$iOp) if(!$iOp)
continue; continue;
$oFilter = new Filter($oOption->getColumn(), $iOp, $sData); $oFilter = new Filter($oOption->getColumn(), $iOp, $sData);
$aReturn[] = $oFilter; $aReturn[] = $oFilter;
} }
if(array_key_exists('i'.$oOption->getColumn()."Op", $aClean)) if(array_key_exists('i'.$this->escapeChars($oOption->getColumn())."Op", $aClean))
{ {
$sColumn = $this->escapeChars($oOption->getColumn());
$i = sizeof($aReturn); $i = sizeof($aReturn);
$sData = $aClean["s{$oOption->getColumn()}Data"]; $sData = $aClean["s{$sColumn}Data"];
$iOp = $aClean["i{$oOption->getColumn()}Op"]; $iOp = $aClean["i{$sColumn}Op"];
if($iOp) if($iOp)
{ {
@@ -208,12 +367,12 @@ class FilterInterface
public function loadTable($sTableName) public function loadTable($sTableName)
{ {
$this->oFilterSet->loadTable(mysql_real_escape_string($sTableName)); $this->oFilterSet->loadTable($sTableName);
} }
public function saveTable($sTableName) public function saveTable($sTableName)
{ {
$this->oFilterSet->saveTable(mysql_real_escape_string($sTableName)); $this->oFilterSet->saveTable($sTableName);
} }
public function getFilterCount() public function getFilterCount()
@@ -221,6 +380,11 @@ class FilterInterface
return $this->oFilterSet->getFilterCount(); return $this->oFilterSet->getFilterCount();
} }
public function getWhereClause()
{
return $this->oFilterSet->getWhereClause();
}
public function getTable($sTable, $iLimit = 0) public function getTable($sTable, $iLimit = 0)
{ {
$hResult = $this->oFilterSet->getMatchedItems($sTable, $iLimit); $hResult = $this->oFilterSet->getMatchedItems($sTable, $iLimit);

View File

@@ -14,6 +14,7 @@ class ObjectManager
private $sReturnTo; private $sReturnTo;
private $sReturnToTitle; /* Used to preserve the title when processing entries from a queue list, for instance */ private $sReturnToTitle; /* Used to preserve the title when processing entries from a queue list, for instance */
private $oMultiPage; private $oMultiPage;
private $oFilters; /* Currently active filters in table view */
private $oTableRow; private $oTableRow;
private $oSortInfo; /* Contains sort info used when displaying tables */ private $oSortInfo; /* Contains sort info used when displaying tables */
private $oObject; /* Store an instance of the object of the class private $oObject; /* Store an instance of the object of the class
@@ -63,6 +64,12 @@ class ObjectManager
return $this->sState; return $this->sState;
} }
public function getFilterInfoFromInput($aClean)
{
if($this->oFilters)
$this->oFilters->readInput($aClean);
}
public function setState($sState) public function setState($sState)
{ {
/* Ensure that the given state is valid */ /* Ensure that the given state is valid */
@@ -126,6 +133,7 @@ class ObjectManager
$this->oMultiPage = new MultiPage(FALSE); $this->oMultiPage = new MultiPage(FALSE);
$this->oTableRow = new OMTableRow(null); $this->oTableRow = new OMTableRow(null);
$this->sState = 'accepted'; $this->sState = 'accepted';
$this->oFilters = $this->getOptionalSetting('objectGetFilterInfo', FALSE);
// initialize the common responses array // initialize the common responses array
$this->aCommonResponses = array(); $this->aCommonResponses = array();
@@ -278,6 +286,8 @@ class ObjectManager
if(!$this->oSortInfo->sCurrentSort) if(!$this->oSortInfo->sCurrentSort)
$this->oSortInfo->sCurrentSort = $this->getOptionalSetting('objectGetDefaultSort', ''); $this->oSortInfo->sCurrentSort = $this->getOptionalSetting('objectGetDefaultSort', '');
$this->handleFilterControls($aClean);
/* query the class for its entries */ /* query the class for its entries */
/* We pass in queue states to tell the object */ /* We pass in queue states to tell the object */
/* if we are requesting a list of its queued objects or */ /* if we are requesting a list of its queued objects or */
@@ -286,23 +296,30 @@ class ObjectManager
$this->oMultiPage->iItemsPerPage, $this->oMultiPage->iItemsPerPage,
$this->oMultiPage->iLowerLimit, $this->oMultiPage->iLowerLimit,
$this->oSortInfo->sCurrentSort, $this->oSortInfo->sCurrentSort,
$this->oSortInfo->bAscending); $this->oSortInfo->bAscending,
$this->oFilters);
/* did we get any entries? */ /* did we get any entries? */
if(!$hResult || query_num_rows($hResult) == 0) if(!$hResult || query_num_rows($hResult) == 0)
{ {
switch($this->getQueueString($this->getIsQueue(), $this->sState == 'rejected')) if($this->oFilters->getFilterCount())
{ {
case "true": echo '<center>No matches found</center>';
echo "<center>The queue for '$this->sClass' is empty</center>"; } else
break; {
case "false": switch($this->getQueueString($this->getIsQueue(), $this->sState == 'rejected'))
echo "<center>No entries of '$this->sClass' are present</center>"; {
break; case "true":
case "rejected": echo "<center>The queue for '$this->sClass' is empty</center>";
echo "<center>No rejected entries of '$this->sClass' are ". break;
"present</center>"; case "false":
break; echo "<center>No entries of '$this->sClass' are present</center>";
break;
case "rejected":
echo "<center>No rejected entries of '$this->sClass' are ".
"present</center>";
break;
}
} }
if($this->GetOptionalSetting("objectShowAddEntry", FALSE)) if($this->GetOptionalSetting("objectShowAddEntry", FALSE))
@@ -1236,6 +1253,9 @@ class ObjectManager
$sUrl .= "&amp;iPage=".$this->oMultiPage->iPage; $sUrl .= "&amp;iPage=".$this->oMultiPage->iPage;
} }
if($this->oFilters)
$sUrl .= $this->oFilters->getUrlData();
if($this->oSortInfo && $this->oSortInfo->sCurrentSort) if($this->oSortInfo && $this->oSortInfo->sCurrentSort)
{ {
$sUrl .= "&amp;sOrderBy={$this->oSortInfo->sCurrentSort}"; $sUrl .= "&amp;sOrderBy={$this->oSortInfo->sCurrentSort}";
@@ -1267,6 +1287,9 @@ class ObjectManager
$this->oMultiPage->iPage."\">\n"; $this->oMultiPage->iPage."\">\n";
} }
if($this->oFilters)
$sReturn .= $this->oFilters->getHiddenFormData();
if($this->sReturnToTitle) if($this->sReturnToTitle)
$sReturn .= "<input type=\"hidden\" name=\"sReturnToTitle\" value=\"".$this->sReturnToTitle."\">\n"; $sReturn .= "<input type=\"hidden\" name=\"sReturnToTitle\" value=\"".$this->sReturnToTitle."\">\n";
@@ -1310,6 +1333,20 @@ class ObjectManager
echo $oTableRow->GetString(); echo $oTableRow->GetString();
} }
private function handleFilterControls($aClean)
{
/* Show filter info */
if($this->oFilters)
{
echo "<form method=\"post\" action=\"".$this->makeUrl()."\" >";
echo $this->oFilters->getEditor();
echo "<br><input type='submit' value='Submit' name='sSubmit' >";
echo "</form>";
}
}
private function handleMultiPageControls($aClean, $bItemsPerPageSelector = TRUE) private function handleMultiPageControls($aClean, $bItemsPerPageSelector = TRUE)
{ {
/* Display multi-page browsing controls (prev, next etc.) if applicable. /* Display multi-page browsing controls (prev, next etc.) if applicable.
@@ -1361,7 +1398,7 @@ class ObjectManager
$sControls .= "</form>"; $sControls .= "</form>";
} }
$iTotalEntries = $oObject->objectGetEntriesCount($this->sState); $iTotalEntries = $oObject->objectGetEntriesCount($this->sState, $this->oFilters);
$iNumPages = ceil($iTotalEntries / $iItemsPerPage); $iNumPages = ceil($iTotalEntries / $iItemsPerPage);
if($iNumPages == 0) if($iNumPages == 0)
$iNumPages = 1; $iNumPages = 1;

View File

@@ -23,6 +23,7 @@ require_once(BASE.'include/application_queue.php');
require_once(BASE.'include/version_queue.php'); require_once(BASE.'include/version_queue.php');
require_once(BASE.'include/testData_queue.php'); require_once(BASE.'include/testData_queue.php');
require_once(BASE.'include/bugs.php'); require_once(BASE.'include/bugs.php');
require_once(BASE.'include/db_filter_ui.php');
/* if we have no valid class name we should abort */ /* if we have no valid class name we should abort */
if(!isset($aClean['sClass'])) if(!isset($aClean['sClass']))
@@ -59,6 +60,7 @@ if(isset($aClean['sReturnTo']))
$oObject->getMultiPageDataFromInput($aClean); $oObject->getMultiPageDataFromInput($aClean);
$oObject->setSortInfo($aClean); $oObject->setSortInfo($aClean);
$oObject->getFilterInfoFromInput($aClean);
$sClass = $oObject->getClass(); $sClass = $oObject->getClass();
$oOtherObject = new $sClass($oObject->getId()); $oOtherObject = new $sClass($oObject->getId());