<?php
/**
 * @package Joomla
 * @subpackage Fabrik
 * @copyright Copyright (C) 2005 Rob Clayburn. All rights reserved.
 * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.php
 */

// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die();

class FabrikModelTablefilter extends JModel {

	function setTableModel($model)
	{
		$this->tableModel =& $model;
	}

	/**
	 * get the table from the tableModel
	 * @see libraries/joomla/application/component/JModel#getTable($name, $prefix, $options)
	 */

	function getTable()
	{
		return $this->tableModel->getTable();
	}

	/**
	 * $$$ rob activetableid set in content plugin only clear filters on active table (otherwise with n tables in article all qs filters are removed)
	 * @return bol - is the table currently being rendered the table that initially triggered the filter
	 */

	protected function activeTable()
	{
		return JRequest::getInt('id') == JRequest::getInt('activetableid') || JRequest::getVar('activetableid') == '';
	}

	/**
	 * this merges session data for the fromForm with any request data
	 * allowing us to filter data results from both search forms and filters
	 *
	 * @return array
	 */

	function getFilters()
	{
		//form or detailed views should not apply filters? what about querystrings to set up the default values?
		if (JRequest::getCmd('view') == 'details' || JRequest::getCmd('view') == 'form') {
			$this->_request = array();
			return $this->_request;
		}
		if (isset($this->_request)) {
			return $this->_request;
		}

		$filters = array();

		// $$$ rob clears all table filters, and does NOT apply any
		// other filters to the table, even if in querystring
		if (JRequest::getVar('clearfilters') == 1 && $this->activeTable()) {
			$this->clearFilters();
			$this->_request = array();
			return $this->_request;
		}

		if (JRequest::getVar('replacefilters') == 1) {
			$this->clearFilters();
		}

		//$$$ fehers The filter is cleared and applied at once without having to clear it first and then apply it (would have to be two clicks).
		//useful in querystring filters if you want to clear old filters and apply new filters
		if ((JRequest::getVar('filterclear') == 1 || JRequest::getVar('resetfilters') == 1) && $this->activeTable()) {
			$this->clearFilters();
		}

		//overwrite filters with querystring filter
		$this->getQuerystringFilters($filters);
		//echo "<pre>after qs ";print_r($filters);echo "</pre>";
		FabrikHelperHTML::debug($filters, 'filter array: after querystring filters');
		$request 	=& $this->getPostFilterArray();
		$this->counter = count(JArrayHelper::getValue($request, 'key', array()));
		//overwrite filters with session filters (fabrik_incsessionfilters set to false in tableModel::getRecordCounts / for facted data counts
		if(JRequest::getVar('fabrik_incsessionfilters', true)) {
			$this->getSessionFilters($filters);
		}
		//echo "<pre>after session";print_r($filters);echo "</pre>";
		FabrikHelperHTML::debug($filters, 'filter array: after session filters');

		//the search form search all has lower priority than the filter search all and search form filters
		$this->getSearchFormSearchAllFilters($filters);

		//overwrite session filters with search form filters

		$this->getSearchFormFilters($filters);
		FabrikHelperHTML::debug($filters, 'filter array: search form');

		//overwrite filters with 'search all' filter
		$this->getSearchAllFilters($filters);

		//finally overwrite filters with post filters
		$this->getPostFilters($filters);

		FabrikHelperHTML::debug($filters, 'filter array: after getpostfilters');
		$this->_request =& $filters;
		FabrikHelperHTML::debug($this->_request, 'filter array');

//echo "<pre>";print_r($filters);echo "</pre>";
		$this->checkAccess($filters);
		return $filters;
	}

	/**
	 * $$$ rob if the filter should not be applied due to its acl level then set its condition so that it
	 will always return true. Do this rather than unsetting the filter - as this removes the selected option
	 from the filter forms field. Can be used in conjunction with a table filter plugin to override a normal fiters option with the
		plugins option, e.g. load all univertisties courses OR [plugin option] load remote courses run by selected university
		e.g http://www.epics-ve.eu/index.php?option=com_fabrik&view=table&tableid=5
		*/

	public function checkAccess(&$filters)
	{
		$access = JArrayHelper::getValue($filters, 'access', array());
		foreach ($access as $key => $selAccess) {
			$i = $filters['key'][$key];
			// $$$ rob - mustApplyFilter is for PREFILTERS and is not right for filters
			//if (!$this->tableModel->mustApplyFilter($selAccess, $i)) {
			if (!FabrikWorker::getACL($selAccess, $i)) {
				$filters['sqlCond'][$key] = '1=1';
			}
		}
		FabrikHelperHTML::debug($filters, 'filter array: after access taken into account');
	}

	/**
	 * check if the search all field (name=fabrik_table_filter_all) has submitted data
	 *
	 * If it has then go through all elements, and add in a filter
	 * for each element whose data type matches the search type
	 * (e.g. if searching a string then ignore int() fields)
	 *
	 * If another filter has posted some data then don't add in a 'search all' record for that filter
	 *
	 * @param array $filters
	 */

	private function getSearchAllFilters(&$filters)
	{
		$app =& JFactory::getApplication();
		$table =& $this->tableModel->getTable();
		$key					= 'com_fabrik.table'. $table->id.'.filter.searchall';
		$search = trim($app->getUserStateFromRequest($key, 'fabrik_table_filter_all'));

		if ($search == '') {
			if (array_key_exists('fabrik_table_filter_all', $_POST)) {
				//empty search string sent unset any searchall filters
				$ks = array_keys($filters);
				$filterkeys = array_keys(JArrayHelper::getValue($filters, 'search_type', array()));
				foreach ($filterkeys as $filterkey) {
					if (JArrayHelper::getValue($filters['search_type'], $filterkey, '') == 'searchall') {
						foreach ($ks as $k) {
							unset($filters[$k][$filterkey]);
						}
					}
				}
			}
		}

		if ($search == '') {
			//clear full text search all
			if (array_key_exists('fabrik_table_filter_all', $_POST)) {
				$this->clearAFilter($filters, 9999);
			}
			return;
		}
		$tableid = JRequest::getInt('tableid', -1);

		// check that we actually have the correct table id (or -1 if filter from viz)
		if ($this->tableModel->getTable()->id == $tableid || $tableid == -1) {
			if ($this->tableModel->getParams()->get('search-mode-advanced')) {
				$this->doBooleanSearch($filters, $search);
			} else {
				$this->insertSearchAllIntoFilters($filters, $search);
			}
		}
	}

	/**
	 * clear specific filter data all from filters
	 * @param array $filters
	 * @param int index
	 */

	public function clearAFilter(&$filters, $id)
	{
		$keys = array_keys($filters);
		foreach($keys as $key) {
			// $$$ hugh - couple of folk have reported getting PHP error "Cannot unset string offsets"
			// which means sometimes $filters->foo is a string.  Putting a bandaid on it for now,
			// but really should try and find out why sometimes we have strings rather than arrays.
			if (is_array($filters[$key])) {
				unset($filters[$key][$id]);
			}
		}
	}

	/**
	 * do a boolean search
	 * @param array $filters
	 * @param string $search
	 */

	private function doBooleanSearch(&$filters, $search)
	{
		$mode = JRequest::getVar('search-mode-advanced', 'and');
		if (trim($search) == '') {
			return;
		}
		$search = explode(' ', $search);

		switch ($mode) {
			case 'all':
				$operator = '+';
				break;
			case 'none':
				$operator = '-';
				break;
			default:
			case 'exact':
			case 'any':
				$operator = '';
				break;
		}
		foreach ($search as &$s) {
			$s = $operator . $s;
		}
		$search = implode(' ', $search);

		if ($mode == 'exact') {
			$search = '"'.$search.'"';
		}

		if ($mode == 'none') {
			//have to do it like this as the -operator removes records matched from previous +operators (so if you just have -operatos)
			// no records are returned
			$search = '+(a* b* c* d* e* f* g* h* i* j* k* l* m* n* o* p* q* r* s* t* u* v* w* x* y* z*) ' . $search;
		}

		JRequest::setVar('overide_join_val_column_concat', 1);
		$names = $this->tableModel->getSearchAllFields();

		if (empty($names)) {
			return;
		}
		JRequest::setVar('overide_join_val_column_concat', 0);
		$names = implode(", ", $names);
		$filters['value'][9999] = $search;
		$filters['condition'][9999] = 'AGAINST';
		$filters['join'][9999] = 'AND';
		$filters['no-filter-setup'][9999] = 0;
		$filters['hidden'][9999] = 0;
		$filters['key'][9999] = "MATCH(".$names.")";
		$filters['key2'][9999] = "MATCH(".$names.")";
		$filters['search_type'][9999] = 'searchall';
		$filters['match'][9999] = 1;
		$filters['full_words_only'][9999] = 0;
		$filters['eval'][9999] = 0;
		$filters['required'][9999] = 0;
		$filters['access'][9999] = 0;
		$filters['grouped_to_previous'][9999] = 1;
		$filters['label'][9999] = '';
		$filters['elementid'][9999] = -1;
		$filters['raw'][9999] = false;
	}

	/**
	 * removes any search or filters from the table
	 */

	private function clearFilters()
	{
		$session =& JFactory::getSession();
		$registry	=& $session->get('registry');
		$tid = 'table'.$this->tableModel->getTable()->id;
		$key					= 'com_fabrik.table.'. $tid.'.filter';

		//$$$rob test1
		if (isset($registry->_registry['com_fabrik']['data']->$tid->filter)) {
			$reg =& $registry->_registry['com_fabrik']['data']->$tid->filter;
			// $$$ rob jpluginfilters search_types are those which have been set inside the
			// Joomla content plugin e.g. {fabrik view=table id=1 tablename___elementname=foo}
			// these should not be removed when the table filters are cleared
			$reg = JArrayHelper::fromObject($reg);
			$serachTypes = JArrayHelper::getValue($reg, 'search_type', array());
			for($i = 0; $i < count($serachTypes); $i++) {
				if ($serachTypes[$i] !== 'jpluginfilters') {
					$this->clearAFilter($reg, $i);
				}
			}
			$reg = JArrayHelper::toObject($reg);

			$registry->_registry['com_fabrik']['data']->$tid->filter = $reg;
			//unset($registry->_registry['com_fabrik']['data']->$tid->filter);
		}

		//reset plugin filter
		if (isset($registry->_registry['com_fabrik']['data']->$tid->plugins)) {
			unset($registry->_registry['com_fabrik']['data']->$tid->plugins);
		}
		$app			=& JFactory::getApplication();
		$key			= 'com_fabrik.'.$tid.'.searchall';
		$v = $app->setUserState($key, '');

		// $$$ rob if resetfilters=1 in url and performing search all search, the mode would
		// always be set to 'and' with this line commented in. Not sure why it was there
		//JRequest::setVar('search-mode-advanced', 'and');
		$fromFormId = $app->getUserState('com_fabrik.searchform.fromForm');
		if ($fromFormId != $this->tableModel->getForm()->_id) {
			$app->setUserState('com_fabrik.searchform.form'.$fromFormId.'.searchall', '');
		}
	}

	/**
	 *
	 * @param array filters
	 * @param string $search
	 * @return null
	 */

	private function insertSearchAllIntoFilters(&$filters, $search)
	{
		$elements =& $this->tableModel->getElements('id', false);
		$keys = array_keys($elements);
		$i = 0;
		// $$$ rob getDefaultFilterCondition() for db joins is "=" which wont work on if your dbjoin's id is on a string
		//$condition =   $elementModel->getDefaultFilterCondition();
		$condition = 'REGEXP';
		$orig_search = $search;
		foreach ($keys as $elid) {
			// $$$ hugh - need to reset $search each time round, in case getFilterValue has esacped something,
			// like foo.exe to foo\\\.exe ... otherwise each time round we double the number of \s's
			$search = $orig_search;
			$elementModel =& $elements[$elid];
			if (!$elementModel->includeInSearchAll()) {
				continue;
			}
			$k = $elementModel->getFullName(false, false, false);
			$eval = FABRIKFILTER_TEXT;

			if (!is_a($elementModel, 'FabrikModelFabrikDatabasejoin')) {
				$fieldDesc = $elementModel->getFieldDescription();


				if (JString::stristr($fieldDesc, 'INT')) {
					if (is_numeric($search) && $condition == '=') {
						$eval = FABRKFILTER_NOQUOTES;
					}
				}
				$k2 = null;
			} else {
				$k2 =  $elementModel->getJoinLabelColumn();
			}
			$element =& $elementModel->getElement();
			$elparams =& $elementModel->getParams();

			$key = array_key_exists('key', $filters) ? array_search($k, $filters['key']) : false;

			// $$$ rob so search all on checkboxes/radio buttons etc will take the search value of 'one' and return '1'
			$newsearch = $elementModel->getFilterValue($search, $condition, $eval);
			$search = $newsearch[0];
			if ($key !== false) {
				$filters['value'][$key] = $search;
				$filters['condition'][$key] = $condition;
				$filters['join'][$key] = 'OR';
				$filters['no-filter-setup'][$key] = ($element->filter_type == '') ? 1 : 0;
				$filters['hidden'][$key] = ($element->filter_type == '') ? 1 : 0;
				$filters['key'][$key] = $k;
				$filters['key2'][$key] = $k2;
				$filters['search_type'][$key] = 'searchall';
				$filters['match'][$key] = 1;
				$filters['full_words_only'][$key] = 0;
				$filters['eval'][$key] = $eval;
				$filters['required'][$key] = 0;
				$filters['access'][$key] = 0;
				$filters['grouped_to_previous'][$key] = $k == 0 ? 0 : 1;
				$filters['label'][$key] = $elparams->get('element_alt_table_heading') == '' ? $element->label : $elparams->get('element_alt_table_heading');
				$filters['raw'][$key] = false;
			} else {
				$filters['value'][] = $search;
				$filters['condition'][] = $condition;
				$filters['join'][] = 'OR';
				$filters['no-filter-setup'][] = ($element->filter_type == '') ? 1 : 0;
				$filters['hidden'][] = ($element->filter_type == '') ? 1 : 0;
				$filters['key'][] = $k;
				$filters['key2'][] = $k2;
				$filters['search_type'][] = 'searchall';
				$filters['match'][] = 1;
				$filters['full_words_only'][] = 0;
				$filters['eval'][] = $eval;
				$filters['required'][] = 0;
				$filters['access'][] = 0;
				//$$$ rob having grouped_to_previous as 1 was barfing this table view for bea, when doing a search all:
				// http://test.xx-factory.de/index.php?option=com_fabrik&view=table&tableid=31&calculations=0&Itemid=16&resetfilters=0
				$filters['grouped_to_previous'][] = 0;//1;
				$filters['label'][] = $elparams->get('element_alt_table_heading') == '' ? $element->label : $elparams->get('element_alt_table_heading');
				$filters['elementid'][] = $element->id;
				$filters['raw'][] = false;
			}
			$i ++;
		}
	}

	private function getSearchFormSearchAllFilters(&$filters)
	{
		//see if there was a search all created from a search form
		$app =& JFactory::getApplication();
		$formModel =& $this->tableModel->getForm();
		$key = 'com_fabrik.searchform.fromForm';
		$fromFormId = $app->getUserState($key);
		if ($fromFormId != $formModel->_id) {
			$search = $app->getUserState('com_fabrik.searchform.form'.$fromFormId.'.searchall');
			if (trim($search) == '') {
				return;
			}
			$this->insertSearchAllIntoFilters($filters, $search);
		}
	}

	private function getSearchFormId()
	{
		$app =& JFactory::getApplication();
		$key	= 'com_fabrik.searchform.fromForm';
		return $app->getUserState($key);
	}

	private function setSearchFormId($id = null)
	{
		$app =& JFactory::getApplication();
		$key	= 'com_fabrik.searchform.fromForm';
		$app->setUserState($key, $id);
	}
	/**
	 *
	 * @param $filters
	 */

	private function getSearchFormFilters(&$filters)
	{
		$fromFormId = $this->getSearchFormId();
		$formModel =& $this->tableModel->getForm();
		$db =& JFactory::getDBO();
		$session =& JFactory::getSession();
		$registry	=& $session->get('registry');
		if (!is_object($registry)) {
			return;
		}
		$lookupkeys = JArrayHelper::getValue($filters, 'key', array());
		if ($fromFormId != $formModel->_id) {
			$fromForm =& JModel::getInstance('Form', 'FabrikModel');
			$fromForm->setId($fromFormId);
			$fromFormParams = $fromForm->getParams();
			//@TODO replace filtername with id
			// $$$ hugh doesn't work!  Added $filter_elements from 'filter_name'
			// which we'll need in the case of $elid not being in $elements for search forms
			$elements =& $this->tableModel->getElements('id');
			$filter_elements =& $this->tableModel->getElements('filtername');
			$key					= 'com_fabrik.searchform.form'.$fromFormId.'.filters';
			$tablename = $db->nameQuote($this->tableModel->getTable()->db_table_name);
			$searchfilters = $registry->getValue($key);
			for ($i = 0; $i < count($searchfilters['key']); $i++) {
				$eval = FABRIKFILTER_TEXT;
				$found = false;
				$key = $searchfilters['key'][$i];
				$elid = $searchfilters['elementid'][$i];
				if (array_key_exists($elid, $elements)) {
					$found = true;
					$elementModel =& $elements[$elid];
				} else {
					// $$$ rob pretty sure that we now key on elid that this is not needed
					// $$$ hugh nope ... still need it ... $elid doesn't exist in $elements when
					// coming from search form.

					//if sent from a search form - the table name will be blank
					$key = $tablename .'.'. array_pop(explode('.', $key));
					if (array_key_exists($key, $filter_elements)) {
						$found = true;
						$elementModel =& $filter_elements["$key"];
					} else {
						//$$$ rob - I've not actually tested this code
						$joins =& $this->tableModel->getJoins();
						foreach ($joins as $join) {
							$key = $db->nameQuote($join->table_join) .'.'. array_pop(explode('.', $key));
							if (array_key_exists($key, $filter_elements)) {
								$found = true;
								$elementModel =& $filter_elements[$key];
								break;
							}
						}
					}
				}
				if (!is_a($elementModel, 'FabrikModelElement') || $found === false) {
					//could be looking for an element which exists in a join
					continue;
				}
				$index = array_key_exists('key', $filters ) ? array_search( $key, $lookupkeys ) : false;
				$element =& $elementModel->getElement();
				$elparams =& $elementModel->getParams();
				$grouped = array_key_exists($i, $searchfilters['grouped_to_previous']) ? $searchfilters['grouped_to_previous'][$i] : 0;

				$join = $searchfilters['join'][$i];
				if ($index === false) {
					$filters['value'][] = $searchfilters['value'][$i];
					$filters['condition'][] = $elementModel->getDefaultFilterCondition();
					$filters['join'][] = $join;
					$filters['no-filter-setup'][] = ($element->filter_type == '') ? 1 : 0;
					$filter['hidden'][] = ($element->filter_type == '') ? 1 : 0;
					$filters['key'][] = $key;
					$filters['search_type'][] = 'search';
					$filters['match'][] = $element->filter_exact_match;
					$filters['full_words_only'][] = $elparams->get('full_words_only');
					$filters['eval'][] = $eval;
					$filters['required'][] = $elparams->get('filter_required');
					$filters['access'][] = $elparams->get('filter_access');
					$filters['grouped_to_previous'][] = $grouped;
					$filters['label'][] = $elparams->get('element_alt_table_heading') == '' ? $element->label : $elparams->get('element_alt_table_heading');
					$filters['raw'][] = false;
				} else {
					unset($lookupkeys[$index]);
					$filters['value'][$index] = $searchfilters['value'][$i];
					$filters['condition'][$index] = $elementModel->getDefaultFilterCondition();
					$filters['join'][$index] = $join;
					$filters['no-filter-setup'][$index] = ($element->filter_type == '') ? 1 : 0;
					$filter['hidden'][$index] = ($element->filter_type == '') ? 1 : 0;
					$filters['key'][$index] = $key;
					$filters['search_type'][$index] = 'search';
					$filters['match'][$index] = $element->filter_exact_match;
					$filters['full_words_only'][$index] = $elparams->get('full_words_only');
					$filters['eval'][$index] = $eval;
					$filters['required'][$index] = $elparams->get('filter_required');
					$filters['access'][$index] = $elparams->get('filter_access');
					$filters['grouped_to_previous'][$index] = $grouped;
					$filters['label'][$index] = $elparams->get('element_alt_table_heading') == '' ? $element->label : $elparams->get('element_alt_table_heading');
					$filters['raw'][$index] = false;
				}
				$filters['elementid'][] = $element->id;
			}
		}
		//unset the search form id so we wont reuse the search data
		//untill a new search is performed
		$this->setSearchFormId(null);
	}

	/**
	 * get any querystring filters that can be applied to the table
	 * you can simple do tablename___elementname=value
	 * or if you want more control you can do
	 *
	 * tablename___elementname[value]=value&tablename_elementname[condition]=OR etc
	 * @param array $filters
	 */

	private function getQuerystringFilters(&$filters)
	{
		$table 		=& $this->tableModel->getTable();
		$request 	= JRequest::get('get');
		$elements =& $this->tableModel->getElements('filtername');
		$filterkeys = array_keys($filters);
		foreach ($request as $key => $val) {
			$oldkey = $key;
			$key = FabrikString::safeColName($key);
			$index = array_key_exists('key', $filters) ? array_search($key, $filters['key']) : false;
			if ($index !== false) {
				foreach ($filterkeys as $fkey) {
					if (is_array($filters[$fkey]) && array_key_exists($index, $filters[$fkey])) {
						//$$$rob test1
						unset($filters[$fkey][$index]);

						//reindex array
						$filters[$fkey] = array_values($filters[$fkey]);
					}
				}
			}
			$raw = 0;
			if (substr($key, -5, 5) == '_raw`') {
				$raw = 1;
			}
			$elementModel =& $elements[$key];
			if (!is_a($elementModel, 'FabrikModelElement')) {
				//check if raw key available

				$key = FabrikString::safeColName(FabrikString::rtrimword($oldkey, '_raw'));
				$elementModel =& $elements[$key];
				if (!is_a($elementModel, 'FabrikModelElement')) {
					continue;
				}
			}
			$eval = FABRIKFILTER_TEXT;

			$condition 	= is_array($val) ? JArrayHelper::getValue($val, 'condition', $elementModel->getDefaultFilterCondition()) : $elementModel->getDefaultFilterCondition();

			if (!is_a($elementModel, 'FabrikModelFabrikDatabasejoin')) {
				$fieldDesc = $elementModel->getFieldDescription();

				if (JString::stristr($fieldDesc, 'INT')) {
					if (is_numeric($val) && $condition == '=') {
						$eval = FABRKFILTER_NOQUOTES;
					}
				}
			}

			//add request filter to end of filter array
			if (is_array($val)) {
				$value 			= JArrayHelper::getValue($val, 'value', '');
				$join 			= JArrayHelper::getValue($val, 'join', 'AND');
				$grouped		= JArrayHelper::getValue($val, 'grouped_to_previous', 0);

				/**
				 * do a ranged querystring search with this syntax
				 * ?element_test___time_date[value][]=2009-08-07&element_test___time_date[value][]=2009-08-10&element_test___time_date[condition]=BETWEEN
				 */
				if (is_array($value) && $condition != 'BETWEEN') { //if we aren't doing a ranged search
					foreach ($value as $vk => $avalue) {
						// if � entered in qs then that is coverted to %E9 which urldecode will convert back
						$value 			= addslashes( urldecode($avalue));
						$acondition = (is_array($condition) && array_key_exists($vk, $condition)) ? $condition[$vk] : $condition;
						$ajoin 			= (is_array($join) && array_key_exists($vk, $join)) ? $join[$vk] : $join;
						$agrouped 	= (is_array($grouped) && array_key_exists($vk, $grouped)) ? $grouped[$vk] : $grouped;
						$this->indQueryString($elementModel, $filters, $avalue, $acondition, $ajoin, $agrouped, $eval, $key, $raw);
					}
				} else {
					$this->indQueryString($elementModel, $filters, $value, $condition, $join, $grouped, $eval, $key);
				}
			} else {
				// if � entered in qs then that is coverted to %E9 which urldecode will convert back
				$value = addslashes( urldecode($val));
				$join = 'AND';
				$grouped = 0;
				$this->indQueryString($elementModel, $filters, $value, $condition, $join, $grouped, $eval, $key, $raw);
			}
		}
	}

	/**
	 * insert individual querystring filter into filter array
	 * @param $elementModel
	 * @param $filters
	 * @param $value
	 * @param $condition
	 * @param $join
	 * @param $grouped
	 * @param $eval
	 * @param $key
	 * @param $raw is the filter a raw filter (tablename___elementname_raw=foo)
	 * @return unknown_type
	 */

	private function indQueryString($elementModel, &$filters, $value, $condition, $join, $grouped, $eval, $key, $raw = false)
	{
		$element =& $elementModel->getElement();
		$elparams =& $elementModel->getParams();
		if (is_string($value)) {
			$value = trim($value);
		}
		$k2 = FabrikString::safeColNameToArrayKey($key);
		// $$$ rob fabrik_sticky_filters set in J content plugin - treat these as prefilters so we dont unset them
		// when we clear the filters
		$filterType = in_array($k2, JRequest::getVar('fabrik_sticky_filters', array())) ? 'jpluginfilters': 'querystring';
		$filters['value'][] = $value;
		$filters['condition'][] =  urldecode($condition);
		$filters['join'][] = $join;
		$filters['no-filter-setup'][] = ($element->filter_type == '') ? 1 : 0;
		$filter['hidden'][] = ($element->filter_type == '') ? 1 : 0;
		$filters['key'][] = $key;
		$filters['key2'][] = '';
		$filters['search_type'][] = $filterType;
		$filters['match'][] = $element->filter_exact_match;
		$filters['full_words_only'][] = $elparams->get('full_words_only');
		$filters['eval'][] = $eval;
		$filters['required'][] = $elparams->get('filter_required');
		$filters['access'][] = $elparams->get('filter_access');
		$filters['grouped_to_previous'][] = $grouped;
		$filters['label'][] = $elparams->get('element_alt_table_heading') == '' ? $element->label : $elparams->get('element_alt_table_heading');
		$filters['elementid'][] = $element->id;
		$filters['raw'][] = $raw;
	}

	private function getPostFilterArray()
	{
		if (!isset($this->request)) {
			$table 		=& $this->tableModel->getTable();
			$request 	= JRequest::get('post');
			if (array_key_exists('fabrik___filter', $request) && array_key_exists('table_'.$table->id, $request['fabrik___filter'])) {
				$this->request = $request['fabrik___filter']['table_'.$table->id];
			} else {
				$this->request = array();
			}
		}
		return $this->request;
	}
	/**
	 * overwrite session and serach all filters with posted data
	 * @param $filters
	 */

	private function getPostFilters(&$filters)
	{
		$table 		=& $this->tableModel->getTable();
		$request 	=& $this->getPostFilterArray();
		$elements =& $this->tableModel->getElements('id');
		$filterkeys = array_keys($filters);
		$values = JArrayHelper::getValue($request, 'value', array());
		$usedMerges = array();
		if (!empty($request) && array_key_exists('key', $request)) {
			$keyints = array_keys($request['key']);
			//for ($i = 0; $i < count($request['key']); $i++) {
			// $$$ rob - in default_horiz_search tmpl only filters whose elements are set to show in table are shown
			// thus the filters may be keyed non-sequentially. Use $keyints rather than count($request[$key]) to ensure
			// that $key is found

			foreach ($keyints as $i) {
				$value = JArrayHelper::getValue($values, $i, '');
				if (is_array($value)) {
					foreach ($value as $k => $v) {
						$value[$k] = urldecode($v);
					}
				}
				else {
					$value = urldecode($value);
				}

				$key = JArrayHelper::getValue($request['key'], $i);
				$elid = JArrayHelper::getValue($request['elementid'], $i);
				if ($key == '') {
					return;
				}
				// index is the filter index for a previous filter that uses the same element id
				if (!in_array($elid, $usedMerges)) {
					$index = array_key_exists('elementid', $filters) ? array_search($elid, (array)$filters['elementid']) : false;
				}else {
					$index = false;
				}
				if($index !== false) {
					$usedMerges[] = $elid;
				}

				//rob empty post filters SHOULD overwrite previous filters, as the user has submitted
				// this filter with nothing selected
				/*if (is_string($value) && trim($value) == '') {
					continue;
					}
					*/

				// $$rob ok the above meant that require filters stopped working as soon as you submitted
				// an empty search!
				// So now  add in the empty search IF there is NOT a previous filter in the search data
				if ((is_string($value) && trim($value) == '') && $index === false) {
					continue;
				}
				// $$$ rob if we are posting an empty value then we really have to clear the filter out from the
				// session. Otherwise the filter is run as "where field = ''"

				if ($value == '' && $index !== false) {
					$this->clearAFilter($filters, $index);
					return;
				}
				$elementModel =& $elements[$elid];
				if (!is_a($elementModel, 'FabrikModelElement')) {
					continue;
				}
				//if the request key is already in the filter array - unset it

				if ($index !== false) {
					foreach ($filterkeys as $fkey) {
						if (is_array($filters[$fkey]) && array_key_exists($index, $filters[$fkey])) {
							// don't unset search all filters when the value is empty and continue so we dont add in a new filter
							if (array_key_exists($index, $filters['search_type']) && ($filters['search_type'][$index] == 'searchall' && $value == '')) {
								continue 2;
							}

							//$$$rob we DO need to unset
							unset($filters[$fkey][$index]);

						}
					}
				}

				//empty ranged data test
				// $$$ hugh - was getting single value array when testing AJAX nav, so 'undefined index 1' warning.
				if (is_array($value) && $value[0] == '' && (!isset($value[1]) || $value[1] == '')) {
					continue;
				}
				$eval = FABRIKFILTER_TEXT;
				if (!is_a($elementModel, 'FabrikModelFabrikDatabasejoin')) {
					$fieldDesc = $elementModel->getFieldDescription();

					if (JString::stristr($fieldDesc, 'INT')) {
						if (is_numeric($value) && $request['condition'][$i] == '=') {
							$eval = FABRKFILTER_NOQUOTES;
						}
					}
				}

				//add request filter to end of filter array
				$element =& $elementModel->getElement();
				$elparams =& $elementModel->getParams();
				$filters['value'][] = $value;
				$filters['condition'][] =  urldecode($request['condition'][$i]);
				$filters['join'][] = strtolower($request['join'][$i]) != 'where' ? $request['join'][$i] : 'AND';
				$filters['no-filter-setup'][] = ($element->filter_type == '') ? 1 : 0;
				$filters['hidden'][] = ($element->filter_type == '') ? 1 : 0;
				$filters['key'][] = urldecode($key);
				$filters['search_type'][] = JArrayHelper::getValue($request['search_type'], $i, 'normal');
				$filters['match'][] = $element->filter_exact_match;
				$filters['full_words_only'][] = $elparams->get('full_words_only');
				$filters['eval'][] = $eval;
				$filters['required'][] = $elparams->get('filter_required');
				$filters['access'][] = $elparams->get('filter_access');
				$filters['grouped_to_previous'][] = $request['grouped_to_previous'][$i];
				$filters['label'][] = $elparams->get('element_alt_table_heading') == '' ? $element->label : $elparams->get('element_alt_table_heading');
				$filters['elementid'][] = $elid;
				$filters['raw'][] = false;
			}
		}
		$this->tableModel->tmpFilters = $filters;
		FabrikHelperHTML::debug($filters, 'filter array: before onGetPostFilter');
		$this->tableModel->getPluginManager()->runPlugins('onGetPostFilter', $this->tableModel, 'table', $filters);
		FabrikHelperHTML::debug($filters, 'filter array: after onGetPostFilter');
		$filters = $this->tableModel->tmpFilters;
	}

	/**
	 * load up filters stored in the session from previous searches
	 * @param array $filters
	 */

	private function getSessionFilters(&$filters)
	{
		$app =& JFactory::getApplication();
		$elements =& $this->tableModel->getElements('id');
		$table =& $this->tableModel->getTable();
		$key					= 'com_fabrik.table'. $table->id.'.filter';
		$sessionfilters = JArrayHelper::fromObject($app->getUserState($key));
		$filterkeys = array_keys($filters);
		if (!is_array($sessionfilters) || !array_key_exists('key', $sessionfilters)) {
			return;
		}

		//if we are coming from a search form ignore session filters
		$fromFormId = $this->getSearchFormId();
		$formModel =& $this->tableModel->getForm();
		if (!is_null($fromFormId) && $fromFormId !== $formModel->_id) {
			return;
		}
		//end ignore
		$request 	=& $this->getPostFilterArray();

		$key					= 'com_fabrik.table'. $table->id.'.filter.searchall';
		$pluginKeys = $this->getPluginFilterKeys();
		$search = $app->getUserStateFromRequest($key, 'fabrik_table_filter_all');

		$postkeys = JArrayHelper::getValue($request, 'key', array());
		for ($i = 0; $i < count($sessionfilters['key']); $i++) {

			$elid = $sessionfilters['elementid'][$i];
			$key = JArrayHelper::getValue($sessionfilters['key'], $i, null);
			$index = JArrayHelper::getValue($filters['elementid'], $key, false);

			//used by radius search plugin
			$sqlConds = JArrayHelper::getValue($sessionfilters, 'sqlCond', array());

			if ($index !== false) {
				foreach ($filterkeys as $fkey) {
					if (is_array($filters[$fkey]) && array_key_exists($index, $filters[$fkey])) {
						//$$$rob test1
						#with the line below uncomment, the unset caused only first filter from query string to work, e..g
						#&element_test___user[value][0]=aaassss&element_test___user[value][1]=X Administrator&element_test___user[join][1]=OR
						#converted to
						#WHERE `jos_users`.`name` REGEXP 'aaassss' OR `jos_users`.`name` REGEXP ' X Administrator'

						//unset($filters[$fkey][$index]);

						//reindex array
						$filters[$fkey] =array_values($filters[$fkey]);
					}
				}
			}
			$value 			= $sessionfilters['value'][$i];
			$key2 			= array_key_exists('key2', $sessionfilters) ? JArrayHelper::getValue($sessionfilters['key2'], $i, '') : '';
			if ($elid == -1) {
				//serach all boolean mode
				$eval = 0;
				$condition = 'AGAINST';
				$join = 'AND';
				$noFiltersSetup = 0;
				$hidden = 0;
				$search_type = 'searchall';
				$match = 1;
				$fullWordsOnly = 0;
				$required = 0;
				$access = 0;
				$grouped = 1;
				$label = '';
				// $$$ rob force the counter to always be the same for advanced search all
				// stops issue of multiple search alls being applied
				$counter = 9999;
				$raw = 0;
				$sqlCond = null;
			} else {
				$elementModel =& $elements[$elid];
				if (!is_a($elementModel, 'FabrikModelElement') && !in_array($elid, $pluginKeys)) {
					continue;
				}
				// check table plugins
				if (in_array($elid, $pluginKeys)) {
					$condition 	= $sessionfilters['condition'][$i];
					$eval = $sessionfilters['eval'][$i];
					$search_type = $sessionfilters['search_type'][$i];
					$join = $sessionfilters['join'][$i];
					$grouped = $sessionfilters['grouped_to_previous'][$i];
					$noFiltersSetup = $sessionfilters['no-filter-setup'][$i];
					$hidden= $sessionfilters['hidden'][$i];
					$match  = $sessionfilters['match'][$i];
					$fullWordsOnly  = $sessionfilters['full_words_only'][$i];
					$required = $sessionfilters['required'][$i];
					$access = $sessionfilters['access'][$i];
					$label = $sessionfilters['label'][$i];
					$sqlCond  = JArrayHelper::getValue($sqlConds, $i);
					$raw = $sessionfilters['raw'][$i];
					$counter = $elid;
				} else {
					$sqlCond = null;
					$condition 	= array_key_exists($i, $sessionfilters['condition']) ? $sessionfilters['condition'][$i] : $elementModel->getDefaultFilterCondition();
					$raw	= array_key_exists($i, $sessionfilters['raw']) ? $sessionfilters['raw'][$i] : 0;
					$eval = FABRIKFILTER_TEXT;
					if (!is_a($elementModel, 'FabrikModelFabrikDatabasejoin')) {
						$fieldDesc = $elementModel->getFieldDescription();

						if (JString::stristr($fieldDesc, 'INT')) {
							if (is_numeric($search) && $condition == '=') {
								$eval = FABRKFILTER_NOQUOTES;
							}
						}
					}
					//add request filter to end of filter array
					//with advanced search and then page nav this wasnt right

					$search_type = array_key_exists($i, $sessionfilters['search_type']) ? $sessionfilters['search_type'][$i] : $elementModel->getDefaultFilterCondition();

					$join 			=  $sessionfilters['join'][$i];
					$grouped = array_key_exists($i, $sessionfilters['grouped_to_previous']) ? $sessionfilters['grouped_to_previous'][$i] : 0;

					$element =& $elementModel->getElement();
					$elparams =& $elementModel->getParams();
					$noFiltersSetup = ($element->filter_type == '') ? 1 : 0;
					$hidden = ($element->filter_type == '') ? 1 : 0;
					$match = $element->filter_exact_match;
					$fullWordsOnly = $elparams->get('full_words_only');
					$required = $elparams->get('filter_required');
					$access = $elparams->get('filter_access');
					$label = $elparams->get('element_alt_table_heading') == '' ? $element->label : $elparams->get('element_alt_table_heading');

					// $$$ rob if the session filter is also in the request data then set it to use the same key as the post data
					//when the post data is processed it should then overwrite these values
					$counter = array_search($key, $postkeys) !== false ? array_search($key, $postkeys) : $this->counter;

				}
			}
			$filters['value'][$counter] = $value;
			$filters['condition'][$counter] =  $condition;
			$filters['join'][$counter] = $join;
			$filters['no-filter-setup'][$counter] = $noFiltersSetup;
			$filters['hidden'][$counter] = $hidden;
			$filters['key'][$counter] = $key;
			$filters['key2'][$counter] = $key2;
			$filters['search_type'][$counter] = $search_type;
			$filters['match'][$counter] = $match;
			$filters['full_words_only'][$counter] = $fullWordsOnly;
			$filters['eval'][$counter] = $eval;
			$filters['required'][$counter] = $required;
			$filters['access'][$counter] = $access;
			$filters['grouped_to_previous'][$counter] = $grouped;
			$filters['label'][$counter] = $label;
			$filters['elementid'][$counter] = $elid;
			$filters['sqlCond'][$counter] = $sqlCond;
			$filters['raw'][$counter] = $raw;
			if (array_search( $key, $postkeys) === false) {
				$this->counter ++;
			}
		}
	}

	/**
	 * get an array of the table's plugin filter keys
	 * @return array of key names
	 */

	public function getPluginFilterKeys()
	{
		$pluginManager =& $this->tableModel->getPluginManager();
		$plugins = $pluginManager->getPlugInGroup('table');
		$keys = array();
		foreach ($plugins as $p) {
			$keys[] = $p->getFilterKey();
		}
		return $keys;
	}

}
?>