diff --git a/include/application.php b/include/application.php
index d9ee999..b8bc19c 100644
--- a/include/application.php
+++ b/include/application.php
@@ -11,6 +11,7 @@ require_once(BASE."include/util.php");
require_once(BASE."include/mail.php");
require_once(BASE."include/maintainer.php");
require_once(BASE."include/tableve.php");
+require_once(BASE."include/db_filter_ui.php");
define("PLATINUM_RATING", "Platinum");
define("GOLD_RATING", "Gold");
@@ -939,11 +940,18 @@ class Application {
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 = "";
$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? */
if($iRows || $iStart)
{
@@ -955,10 +963,10 @@ class Application {
$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
AND
- appFamily.state = '?'";
+ appFamily.state = '?'$sWhereFilter";
if($sState != 'accepted' && !application::canEdit())
{
@@ -997,6 +1005,14 @@ class Application {
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()
{
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())
{
/* Without edit rights users can only resubmit their rejected entries */
if(!$bRejected)
return FALSE;
- $sQuery = "SELECT COUNT(appId) as count FROM appFamily WHERE
+ $sQuery = "SELECT COUNT(DISTINCT(appFamily.appId)) as count FROM appFamily$sExtraTables WHERE
submitterId = '?'
AND
- state = '?'";
+ appFamily.state = '?'$sWhereFilter";
$hResult = query_parameters($sQuery, $_SESSION['current']->iUserId,
$sState);
} 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);
}
diff --git a/include/db_filter.php b/include/db_filter.php
index 2ae001a..e5a556d 100644
--- a/include/db_filter.php
+++ b/include/db_filter.php
@@ -14,6 +14,7 @@ define('FILTER_GREATER_THAN', 3);
define('FILTER_LESS_THAN', 4);
define('FILTER_NOT_EQUALS', 5);
define('FILTER_NOT_LIKE', 6);
+define('FILTER_ENUM', 7);
/* A filter as part of an SQL query, such as something = 'somevalue' */
class Filter
diff --git a/include/db_filter_ui.php b/include/db_filter_ui.php
index 71c90c9..d928b84 100644
--- a/include/db_filter_ui.php
+++ b/include/db_filter_ui.php
@@ -10,6 +10,10 @@
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,
and what comparison options are available */
class FilterInfo
@@ -17,12 +21,22 @@ 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)
+ 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()
@@ -35,6 +49,21 @@ class FilterInfo
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;
@@ -65,11 +94,15 @@ class FilterInterface
{
private $aFilterInfo;
private $oFilterSet;
+ private $aEscapeChars;
+ private $aEscapeCharsWith;
public function FilterInterface($sTableName = '')
{
$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)
@@ -83,15 +116,53 @@ class FilterInterface
}
/* 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 = "getOperatorId()}\">";
+ $shEditor .= "getData()}\" />";
+
+ return $shEditor;
+ }
+
+
public function getItemEditor($iId, Filter $oFilter)
{
- $sColumn = $oFilter->getColumn();
- $oColumn = $this->aFilterInfo[$sColumn];
+ $sColumn = $this->escapeChars($oFilter->getColumn());
+ $oColumn = $this->aFilterInfo[$oFilter->getColumn()];
$sId = ($iId == -1) ? '' : $iId;
$shEditor = $oColumn->getDisplayName();
@@ -121,7 +192,90 @@ class FilterInterface
$shEditor .= ' ';
- $shEditor .= "getData()}\" name='s{$sColumn}Data$sId' size='30' />";
+ switch($oColumn->getValueType())
+ {
+ case FILTER_VALUES_NORMAL:
+ $shEditor .= "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 .= "";
+
+ 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;
}
@@ -131,16 +285,18 @@ class FilterInterface
$shEditor = '';
$aCounts = array();
- $shEditor .= 'Add new filter
';
+ $shEditor .= 'Add new filter (You don’t have to fill out all rows.)
';
foreach($this->aFilterInfo as $oOption)
{
$oDummyFilter = new Filter($oOption->getColumn(), 0, '');
- $shEditor .= $this->getItemEditor(-1, $oDummyFilter);
+ $aTypes = $oOption->getTypes();
+
+ $shEditor .= $this->getItemEditor(-1, $oDummyFilter);
$shEditor .= '
';
}
if(sizeof($this->oFilterSet->getFilters()))
- $shEditor .= '
Active filters
';
+ $shEditor .= '
Active filters
';
foreach($this->oFilterSet->getFilters() as $oFilter)
{
$sColumn = $oFilter->getColumn();
@@ -167,23 +323,26 @@ class FilterInterface
{
$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"]);
- $iOp = $aClean["i{$oOption->getColumn()}Op$i"];
+ $sColumn = $this->escapeChars($oOption->getColumn());
+ $sData = query_escape_string($aClean["s{$sColumn}Data$i"]);
+ $iOp = $aClean["i{$sColumn}Op$i"];
if(!$iOp)
continue;
$oFilter = new Filter($oOption->getColumn(), $iOp, $sData);
+
$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);
- $sData = $aClean["s{$oOption->getColumn()}Data"];
- $iOp = $aClean["i{$oOption->getColumn()}Op"];
+ $sData = $aClean["s{$sColumn}Data"];
+ $iOp = $aClean["i{$sColumn}Op"];
if($iOp)
{
@@ -208,12 +367,12 @@ class FilterInterface
public function loadTable($sTableName)
{
- $this->oFilterSet->loadTable(mysql_real_escape_string($sTableName));
+ $this->oFilterSet->loadTable($sTableName);
}
public function saveTable($sTableName)
{
- $this->oFilterSet->saveTable(mysql_real_escape_string($sTableName));
+ $this->oFilterSet->saveTable($sTableName);
}
public function getFilterCount()
@@ -221,6 +380,11 @@ class FilterInterface
return $this->oFilterSet->getFilterCount();
}
+ public function getWhereClause()
+ {
+ return $this->oFilterSet->getWhereClause();
+ }
+
public function getTable($sTable, $iLimit = 0)
{
$hResult = $this->oFilterSet->getMatchedItems($sTable, $iLimit);
diff --git a/include/objectManager.php b/include/objectManager.php
index 32c48fb..cec6fac 100644
--- a/include/objectManager.php
+++ b/include/objectManager.php
@@ -14,6 +14,7 @@ class ObjectManager
private $sReturnTo;
private $sReturnToTitle; /* Used to preserve the title when processing entries from a queue list, for instance */
private $oMultiPage;
+ private $oFilters; /* Currently active filters in table view */
private $oTableRow;
private $oSortInfo; /* Contains sort info used when displaying tables */
private $oObject; /* Store an instance of the object of the class
@@ -63,6 +64,12 @@ class ObjectManager
return $this->sState;
}
+ public function getFilterInfoFromInput($aClean)
+ {
+ if($this->oFilters)
+ $this->oFilters->readInput($aClean);
+ }
+
public function setState($sState)
{
/* Ensure that the given state is valid */
@@ -126,6 +133,7 @@ class ObjectManager
$this->oMultiPage = new MultiPage(FALSE);
$this->oTableRow = new OMTableRow(null);
$this->sState = 'accepted';
+ $this->oFilters = $this->getOptionalSetting('objectGetFilterInfo', FALSE);
// initialize the common responses array
$this->aCommonResponses = array();
@@ -278,6 +286,8 @@ class ObjectManager
if(!$this->oSortInfo->sCurrentSort)
$this->oSortInfo->sCurrentSort = $this->getOptionalSetting('objectGetDefaultSort', '');
+ $this->handleFilterControls($aClean);
+
/* query the class for its entries */
/* We pass in queue states to tell the object */
/* if we are requesting a list of its queued objects or */
@@ -286,23 +296,30 @@ class ObjectManager
$this->oMultiPage->iItemsPerPage,
$this->oMultiPage->iLowerLimit,
$this->oSortInfo->sCurrentSort,
- $this->oSortInfo->bAscending);
+ $this->oSortInfo->bAscending,
+ $this->oFilters);
/* did we get any entries? */
if(!$hResult || query_num_rows($hResult) == 0)
{
- switch($this->getQueueString($this->getIsQueue(), $this->sState == 'rejected'))
+ if($this->oFilters->getFilterCount())
{
- case "true":
- echo "