Your IP : 10.10.0.253


Current Path : /var/www/administrator/components/com_contactpush/classes/model/
Upload File :
Current File : /var/www/administrator/components/com_contactpush/classes/model/list.php

<?php
/**                               ______________________________________________
*                          o O   |                                              |
*                 (((((  o      <    Generated with Cook Self Service  V3.1     |
*                ( o o )         |______________________________________________|
* --------oOOO-----(_)-----OOOo---------------------------------- www.j-cook.pro --- +
* @version		1.0.0
* @package		Contact Push
* @subpackage	Contact Push
* @copyright	Netamity 2017
* @author		Andy Hickey - www.netamity.com - andy@netamity.com
* @license		GPL V2+
*
*             .oooO  Oooo.
*             (   )  (   )
* -------------\ (----) /----------------------------------------------------------- +
*               \_)  (_/
*/

// no direct access
defined('_JEXEC') or die('Restricted access');

jimport('joomla.application.component.modellist');


/**
* Contactpush List Model
*
* @package	Contactpush
* @subpackage	Classes
*/
class ContactpushCkClassModelList extends JModelList
{
	/**
	* Data array
	*
	* @var array
	*/
	protected $_data = null;

	/**
	* Pagination object
	*
	* @var object
	*/
	protected $_pagination = null;

	/**
	* Total
	*
	* @var integer
	*/
	protected $_total = null;

	/**
	* Context string for the model type.  This is used to handle uniqueness
	*
	* @var string
	*/
	protected $context = null;

	/**
	* Extension name.
	*
	* @var string
	*/
	protected static $extension = 'contactpush';

	/**
	* Filterable fields keys
	*
	* @var array
	*/
	protected $filter_vars = array();

	/**
	* ORM system
	*
	* @var ClassModelOrm
	*/
	protected $orm;

	/**
	* Model relations.
	*
	* @var array
	*/
	protected $relations = array();

	/**
	* Search entries
	*
	* @var array
	*/
	protected $search_vars = array();

	/**
	* Constructor
	*
	* @access	public
	* @param	array	$config	An optional associative array of configuration settings.
	*
	* @return	void
	*/
	public function __construct($config = array())
	{
		parent::__construct($config);

		// Load the ORM system
		$this->orm = ContactpushClassModelOrm::getInstance($this);

		$layout = $this->getLayout();
		$jinput = JFactory::getApplication()->input;
		$render = $jinput->get('render', null, 'CMD');

		$this->context = strtolower($this->option . '.' . $this->getName()
					. ($layout?'.' . $layout:'')
					. ($render?'.' . $render:'')
					);
	}

	/**
	* Method to exclude items from the list.
	*
	* @access	public
	* @param	array	$ids	IDs of the items to restricts
	*
	*
	* @since	Cook 3.0.9
	*
	* @return	void
	*/
	public function addExclude($ids)
	{
		// Merge all exclusions
		$exclude = $this->getState('query.exclude');
		if (is_array($exclude))
			$exclude = array_merge($exclude, $ids);
		else
			$exclude = $ids;

		$this->setState('query.exclude', $exclude);
	}

	/**
	* Method to store an EXTRA at the end of the SQL query. (LIMIT for example)
	*
	* @access	public
	* @param	string	$extra	
	*
	*
	* @deprecated	1
	*
	* @return	void
	*/
	public function addExtra($extra)
	{
		$this->addQuery('extra', $extra);
	}

	/**
	* Method to store a PRIORITARY ORDER for the SQL query. Used to group the
	* fields.
	* Deprecated : use addGroupOrder()
	*
	* @access	public
	* @param	string	$groupby	
	*
	* @return	void
	*/
	public function addGroupBy($groupby)
	{
		$this->addQuery('groupBy', $groupby);
	}

	/**
	* Method to store a PRIORITARY ORDER for the SQL query. Used to group the
	* fields per value.
	*
	* @access	public
	* @param	string	$groupOrder	
	*
	* @return	void
	*/
	public function addGroupOrder($groupOrder)
	{
		$this->addQuery('groupOrder', $this->sanitizeOrdering($groupOrder));
	}

	/**
	* Method to store a JOIN entry for the SQL query.
	*
	* @access	public
	* @param	string	$join	
	* @param	string	$type	
	*
	* @return	void
	*/
	public function addJoin($join, $type = 'left')
	{
		$join = preg_replace("/^((LEFT)?(RIGHT)?(INNER)?(OUTER)?\sJOIN)/", "", $join);
		$this->addQuery('join.' . strtolower($type), $join);
	}

	/**
	* Method to store an ORDER entry for the SQL query.
	*
	* @access	public
	* @param	string	$order	
	*
	* @return	void
	*/
	public function addOrder($order)
	{
		$this->addQuery('order', $this->sanitizeOrdering($order));
	}

	/**
	* Concat SQL parts in query. (Suggested by Cook Self Service)
	*
	* @access	public
	* @param	string	$type	SQL command.
	* @param	string	$queryElement	Command content.
	*
	* @return	void
	*/
	public function addQuery($type, $queryElement)
	{
		$queryElement = trim($queryElement);
		$queries = $this->getState('query.' . $type, array());
		if (!in_array($queryElement, $queries))
		{
			$queries[] = $queryElement;
			$this->setState('query.' . $type, $queries);
		}
	}

	/**
	* Method to restricted a list of items inside a list of possibles values.
	*
	* @access	public
	* @param	array	$ids	IDs of the items to restricts
	*
	*
	* @since	Cook 3.0.9
	*
	* @return	void
	*/
	public function addRestrict($ids)
	{
		// Intersects all restrictions
		$restrict = $this->getState('query.restrict');
		if (is_array($restrict))
			$restrict = array_intersect($restrict, $ids);
		else
			$restrict = $ids;

		$this->setState('query.restrict', $restrict);
	}

	/**
	* Method to concat a search entry.
	*
	* @access	public
	* @param	string	$instance	
	* @param	string	$namespace	
	* @param	string	$method	
	*
	* @return	void
	*/
	public function addSearch($instance, $namespace, $method)
	{
		$search = new stdClass();
		$search->method = $method;


		if (!isset($this->_searches[$instance]))
			$this->_searches[$instance] = array();

		$this->_searches[$instance][$namespace] = $search;
	}

	/**
	* Method to store a SELECT entry for the SQL query.
	*
	* @access	public
	* @param	string	$select	
	*
	* @return	void
	*/
	public function addSelect($select)
	{
		$this->addQuery('select', $select);
	}

	/**
	* Method to store a WHERE entry for the SQL query.
	*
	* @access	public
	* @param	string	$where	
	*
	* @return	void
	*/
	public function addWhere($where)
	{
		$this->addQuery('where', $where);
	}

	/**
	* Apply all SQL directives states to the query. This way you can set twice
	* the same statement without error, in case of numerous sql profiles and
	* complex statements.
	* Very important in case of doubled statement they must match exactly the
	* same.
	*
	* @access	protected
	* @param	object	$query	Joomla query object
	*
	*
	* @since	Cook 2.7
	*
	* @return	void
	*/
	protected function applySqlStates($query)
	{
		//Populate only uniques strings to the query

		// SELECT
		foreach($this->getState('query.select', array()) as $select)
			$query->select($select);

		// JOIN LEFT
		foreach($this->getState('query.join.left', array()) as $join)
			$query->join('LEFT', $join);

		// JOIN INNER
		foreach($this->getState('query.join.inner', array()) as $join)
			$query->join('INNER', $join);

		// JOIN OUTER
		foreach($this->getState('query.join.outer', array()) as $join)
			$query->join('OUTER', $join);

		// WHERE
		foreach($this->getState('query.where', array()) as $where)
			$query->where($where);

		// RESTRICT
		if (is_array($this->getState('query.restrict')))
		{
			$restrict = $this->getState('query.restrict');
			if (count($restrict))
				$query->where("a.id IN (" . implode(',', $restrict). ")");
			else
				$query->where("a.id = -1");  // Return nothing (restricted all)
		}

		// EXCLUDE
		if (is_array($this->getState('query.exclude')))
		{
			$exclude = $this->getState('query.exclude');
			if (count($exclude))
				$query->where("a.id NOT IN (" . implode(',', $exclude). ")");
		}

		// GROUP BY : Native SQL Group By
		foreach($this->getState('query.groupBy', array()) as $groupBy)
			$query->group($groupBy);

		// GROUP ORDER : Prioritary order for groups in lists
		foreach($this->getState('query.groupOrder', array()) as $groupOrder)
			$query->order($groupOrder);

		// ORDER
		foreach($this->getState('query.order', array()) as $order)
			$query->order($order);
	}

	/**
	* Method to get all local items matching a search over a pivot relation
	* (N:m).
	*
	* @access	protected
	* @param	object	$relation	Relation to the search
	* @param	array	$conditions	Match at least one of those SQL conditions. Use : <br/>`a` alias for the Foreign Table, <br/>`p` alias for the Pivot Table
	*
	*
	* @since	Cook 3.0.9
	*
	* @return	array	Item ids matching the search.
	*/
	protected function belongsOf($relation, $conditions)
	{
		if ($relation->type != 'belongsToMany')
			return null;

		$db = JFactory::getDbo();
		$query = $db->getQuery(true);

		// Only works from the pivot table (alias: `p`)
		$query->select('p.' . $relation->pivotLocalKey);
		$query->from($relation->pivotTable . ' AS p');

		// Link the Foreign Table
		$query->join('LEFT', $db->qn($relation->foreignTable) . ' AS a '
			. ' ON a.' . $relation->foreignKey . ' = p.' . $relation->pivotForeignKey
		);

		// Concat all conditions with a 'OR' statement
		if (!empty($conditions))
		{
			$condition = '(' . implode(' OR ', $conditions) . ')';
			$query->where($condition);
		}

		$db->setQuery($query);
		$ids = $db->loadColumn();

		return $ids;
	}

	/**
	* Method to initialize a Many to Many relation.
	*
	* @access	protected
	* @param	string	$name	Relation name.
	* @param	string	$foreignModelClass	Foreign model. Use the name when null.
	* @param	string	$localKey	The local key.
	* @param	string	$foreignKey	The foreign key.
	* @param	string	$pivot	The pivot model name. Can recieve namespaced name or raw table name.
	* @param	string	$pivotLocalKey	The pivot local key.
	* @param	string	$pivotForeignKey	The pivot foreign key.
	* @param	array	$selectFields	The required fields to include in select.
	* @param	boolean	$raw	Uses a raw implementation. More optimized, but choose false for using the model features.
	*
	* @return	void
	*/
	protected function belongsToMany($name, $foreignModelClass = null, $localKey = null, $foreignKey = null, $pivot, $pivotLocalKey, $pivotForeignKey, $selectFields = array(), $raw = false)
	{
		$relation = $this->newRelation('belongsToMany', $name, $foreignModelClass, $selectFields);

		$relation->localKey = $localKey;
		$relation->foreignKey = $foreignKey;

		$extension = $relation->extension;
		if (substr($pivot, 0, 1) == '#')
		{
			$relation->pivotTable = $pivot;
		}
		else
		{
			$parts = explode('.', $pivot);
			if (count($parts) > 1)
			{
				$extension = ltrim($parts[0], 'com_');
				$pivot = $parts[1];
				$relation->pivotExtension = $extension;
			}

			$relation->pivotTable = '#__' . $extension . '_' . $pivot;
			$relation->pivotModel = $pivot;
		}

		$relation->pivotLocalKey = $pivotLocalKey;
		$relation->pivotForeignKey = $pivotForeignKey;
		$relation->pivotForeignPkey = 'id';

		$relation->raw = $raw;
	}

	/**
	* Method to build a SQL search string.
	*
	* @access	protected
	* @param	string	$instance	
	* @param	string	$searchText	
	* @param	string	$options	
	*
	* @return	string	The formated SQL string for the research.
	*/
	protected function buildSearch($instance, $searchText, $options = array('join' => 'AND', 'ignoredLength' => 0))
	{
		if (!isset($this->_searches[$instance]))
			return;

		$db= JFactory::getDBO();
		$tests = array();
		foreach($this->_searches[$instance] as $namespace => $search)
		{
			$test = "";
			switch($search->method)
			{
				case 'like':
					$test = $namespace . " LIKE " . $db->Quote("%%s%");
					break;

				case 'exact':
					$test = $namespace . " = " . $db->Quote("%s");
					break;

				case '':
					break;
			}

			if ($test)
				$tests[] = $test;
		}

		if (!count($tests))
			return "";

		$whereSearch = implode(" OR ", $tests);

		//SPLIT SEARCHED TEXT
		$searchesParts = array();

		foreach(explode(" ", $searchText) as $searchStr)
		{
			$searchStr = trim($searchStr);
			if ($searchStr == '')
				continue;

			if ((isset($options['ignoredLength'])) && (strlen($searchStr) <= $options['ignoredLength']))
				continue;

			if ($search->method == 'like')
				$searchStr = $db->escape($searchStr);


			$searchesParts[] = "(" . str_replace("%s", $searchStr, $whereSearch) . ")";
		}

		if (!count($searchesParts))
			return;

		if (isset($options['join']))
			$join = strtoupper($options['join']);
		else
			$join = "AND";

		$where = implode(" " . $join . " ", $searchesParts);

		return $where;
	}

	/**
	* Check if the user can access to the configuration.
	*
	* @access	public
	*
	* @return	boolean	True if allowed.
	*/
	public function canAdmin()
	{
		$acl = ContactpushHelper::getActions();

		if ($acl->get('core.admin'))
			return true;

		return false;
	}

	/**
	* Check if the user can create new items.
	*
	* @access	public
	*
	* @return	boolean	True if allowed.
	*/
	public function canCreate()
	{
		$acl = ContactpushHelper::getActions();
		
		if ($acl->get('core.create'))
			return true;
		
		return false;
	}

	/**
	* Method to test whether a user can delete items.
	*
	* @access	public
	*
	* @return	boolean	True if allowed.
	*/
	public function canDelete()
	{
		$acl = ContactpushHelper::getActions();
		
		if ($acl->get('core.delete'))
			return true;

		if ($acl->get('core.delete.own'))
			return true;
		
		return false;
	}

	/**
	* Check if the user can edit items.
	*
	* @access	public
	*
	* @return	boolean	True if allowed.
	*/
	public function canEdit()
	{
		$acl = ContactpushHelper::getActions();
		
		if ($acl->get('core.edit'))
			return true;

		if ($acl->get('core.edit.own'))
			return true;
		
		return false;
	}

	/**
	* Check if the user can edit the states (publish, default, ...).
	*
	* @access	public
	*
	* @return	boolean	True if allowed.
	*/
	public function canEditState()
	{
		$acl = ContactpushHelper::getActions();
		
		if ($acl->get('core.edit.state'))
			return true;
		
		return false;
	}

	/**
	* Check if allowed to process any acl task.
	*
	* @access	public
	*
	* @return	boolean	True if allowed.
	*/
	public function canSelect()
	{
		if ($this->canAdmin())
		return true;

		if ($this->canEdit())
		return true;

		if ($this->canDelete())
		return true;

		if ($this->canEditState())
		return true;

		if ($this->canEditState())
		return true;

		return false;
	}

	/**
	* Filter a FK value with multiple values.
	*
	* @access	protected
	* @param	string	$namespace	Namespace of the Foreign Keys chain
	*
	*
	* @since	Cook 3.0.10
	*
	* @return	void
	*/
	protected function filterMulti($namespace)
	{
		$parts = explode('.', $namespace);

		$ids = array();
		if ($values = $this->getState('filter.' . implode('_', $parts)))
		{
			if (is_array($values))
			{
				// Cast and Remove the first empty value
				foreach($values as $val)
					if (!empty($val))
						$ids[] = (int)$val;
			}
		}

		$this->prepareQueryJoin($namespace);

		if (count($ids))
			$this->addWhere($this->tableFieldAlias($namespace) . " IN (" . implode(',', $ids) . ")");
	}

	/**
	* Method to filter a list with a Pivot relation (N:m).
	*
	* @access	protected
	* @param	string	$relationName	Name of the model relation
	* @param	string	$stateName	Name of the values state. You can use any state var name for custom. <br/> By default, reads the value from the pivot filter state.
	*
	*
	* @since	Cook 3.0.9
	*
	* @return	void
	*/
	protected function filterPivot($relationName, $stateName = null)
	{
		if (!$stateName)
			$stateName = 'filter.' . $relationName;


		if($values = $this->getState($stateName))
		{
			if (is_array($values))
			{
				$relation = $this->getRelation($relationName);

				// @logic Determines the behaviour of the filter
				$logic = strtoupper($this->getState($stateName . '.logic', 'AND'));
				switch ($logic)
				{
					case 'NOT':
					case 'OR':
						// Matches "at least one" value
						$conditions = array();
						foreach($values as $value)
						{
							if (empty($value))
								continue;

							$conditions[] = 'a.' . $relation->foreignKey . ' = ' . (int)$value;
						}

						if (count($conditions))
						{
							// Search for all related items matching the filter values
							$ids = $this->belongsOf($relation, $conditions);

							if ($logic == 'OR')
								// SQL Restriction list from the related items founds
								$this->addRestrict($ids);
							else if ($logic == 'NOT')
								// SQL Exclusion of the related items founds.
								$this->addExclude($ids);
						}

						break;

					case 'AND':
					default:

						// Match ALL values
						foreach($values as $value)
						{
							if (empty($value))
								continue;


							// Search for all related items matching the filter values
							$ids = $this->belongsOf($relation, array('a.' . $relation->foreignKey . ' = ' . (int)$value));

							// Restrict SQL list from the related items founds
							$this->addRestrict($ids);


						}

						break;
				}

			}
		}

	}

	/**
	* Temporary function, before FoF implementation. Return the table Foreign Key
	* name of a field.
	*
	* @access	public static
	* @param	string	$fieldname	FK field name
	*
	*
	* @since	Cook 2.6.3
	*
	* @return	string	The table name. # is used as prefix to significate the component name table prefix.
	*/
	public static function fkTable($fieldname)
	{
		$tbl = '#__';
		$com = 'contactpush_';

		switch($fieldname)
		{
			case 'created_by': return $tbl. 'users';
			case 'access': return $tbl. 'viewlevels';
			case 'message': return $tbl.$com. 'messages';
			case 'user': return $tbl. 'users';	
		}
	}

	/**
	* Method to get a customized form.
	*
	* @access	public
	* @param	string	$instance	The name of the form in XML file.
	* @param	boolean	$loadData	True if the form is to load its own data (default case), false if not.
	* @param	string	$control	The name of the control group.
	*
	*
	* @since	Cook 2.0
	*
	* @return	JXMLElement	A Fieldset containing all the field parameters (XML node)
	*/
	public function getForm($instance = 'default.filters', $loadData = true, $control = null)
	{
		$model = CkJModel::getInstance($this->view_item, 'ContactpushModel');
		$form = $model->getForm(null, $loadData, $control);

		if (empty($form))
			return null;

		if ($loadData)
		{
			//Fill the form with the states vars (For filters)
			foreach ($this->filter_vars as $filterVar => $type)
			{
				switch($filterVar)
				{
					case 'sortTable':
						$fieldName = $filterVar;
						$stateVar = 'list.ordering';
						break;

					case 'directionTable':
						$fieldName = $filterVar;
						$stateVar = 'list.direction';
						break;

					case 'limit':
						$fieldName = $filterVar;
						$stateVar = 'list.limit';
						break;

					default:
						$fieldName = 'filter_' . $filterVar;
						$stateVar = 'filter.' . $filterVar;
						break;
				}
				$value = $this->getState($stateVar);

				$form->setValue($fieldName, '', $value);
			}

			//Fill the form with the states vars (For Searches)
			foreach ($this->search_vars as $searchVar => $type)
			{
				$value = $this->getState('search.' . $searchVar);
				$form->setValue('search_' . $searchVar, '', $value);
			}
		}

		$fieldSet = $form->getFieldset($instance);

		//Check ACL (access property)
		$allowedFields = array();
		foreach($fieldSet as $name => $field)
		{
			if ((method_exists($field, 'canView')) && !$field->canView())
				continue;

			$allowedFields[$name] = $field;
		}
		return $allowedFields;
	}

	/**
	* Method to get an array of data items. Override to catch the errors.
	*
	* @access	public
	*
	*
	* @since	11.1
	*
	* @return	array	Items objects.
	*/
	public function getItems()
	{
		try
		{
			$items = parent::getItems();
			$db = $this->getDbo();

			if ($error = $db->getErrorMsg())
			{
				if (!$this->canAdmin())
					$error = JText::_('CONTACTPUSH_ERROR_INVALID_QUERY');
				throw new Exception($error);
			}

			// Complete the Item shape with some flags
			$this->populateParams($items);

			// Create linked objects (N:m)
			$this->populateObjects($items);

		}
		catch (JException $e)
		{

		}
		return $items;
	}

	/**
	* Get the current layout. Abstract function to override.
	*
	* @access	public
	*
	*
	* @since	11.1
	*
	* @return	string	The default layout alias.
	*/
	public function getLayout()
	{
		$jinput = JFactory::getApplication()->input;
		return $jinput->get('layout', 'default', 'STRING');
	}

	/**
	* Method to get a JDatabaseQuery object for retrieving the data set from a
	* database.
	*
	* @access	public
	*
	*
	* @since	11.1
	*
	* @return	JDatabaseQuery	A JDatabaseQuery object to retrieve the data set.
	*/
	public function getListQuery()
	{
		$db = $this->getDbo();
		$query = $db->getQuery(true);
		$this->prepareQuery($query);
		return $query;
	}

	/**
	* Proxy to get the model.
	*
	* @access	public
	* @param	bool	$item	If true, return the item model
	*
	*
	* @since	1.6
	*
	* @return	JModel	Return the model.
	*/
	public function getModel($item = false)
	{
		if ($item)
			return CkJModel::getInstance($this->view_item, 'ContactpushModel');

		return parent::getModel();
	}

	/**
	* Get the model singular name.
	*
	* @access	public
	*
	*
	* @since	1.6
	*
	* @return	string	Return the model singular.
	*/
	public function getNameItem()
	{
		return $this->view_item;
	}

	/**
	* Get the model primary key name.
	*
	* @access	public
	*
	*
	* @since	2.7.3
	*
	* @return	string	Model primary key name.
	*/
	public function getNamePk()
	{
		return 'id';
	}

	/**
	* Read ORM configuration from XML Fiters file. Not available yet.
	*
	* @access	public
	*
	*
	* @since	3.1
	*
	* @return	array	ORM description.
	*/
	public function getOrmFilters()
	{

	}

	/**
	* Method to get a registered relation in list model.
	*
	* @access	public
	* @param	string	$name	Relation name. If null, returns all relations.
	*
	* @return	mixed	A relation object. Null if not found. All relation if name is null.
	*/
	public function getRelation($name = null)
	{
		if (!$name)
			// Return all relations
			return $this->relations;

		if (!isset($this->relations[$name]))
			return;

		return $this->relations[$name];
	}

	/**
	* Get all the known relations of the model.
	*
	* @access	public
	*
	*
	* @since	3.1
	*
	* @return	array	Set of relations maps objects.
	*/
	public function getRelations()
	{
		return $this->relations;
	}

	/**
	* Alternative to avoid userVar beeing updated for Ajax calls.
	*
	* @access	public
	* @param	string	$key	The key of the user state variable.
	* @param	string	$request	The name of the variable passed in a request.
	* @param	string	$default	The default value for the variable if not found. Optional.
	* @param	string	$type	Filter for the variable, for valid values see {@link JFilterInput::clean()}. Optional.
	* @param	string	$resetPage	If true, the limitstart in request is set to zero
	*
	* @return	void
	*/
	public function getUserStateFromRequest($key, $request, $default = null, $type = 'none', $resetPage = true)
	{
		$app = JFactory::getApplication();
		$jinput = JFactory::getApplication()->input;

		$old_state = $app->getUserState($key);
		$cur_state = (!is_null($old_state)) ? $old_state : $default;

		$new_state = $jinput->get($request, $cur_state, $type);

		//Only POST queries can apply changes on the states vars. Ajax queries or JSON requests are also excluded from storing states in session
		if (($jinput->getMethod() == 'POST')
			&& ($jinput->get('layout') != 'ajax')
			&& (!in_array($jinput->get('format'), array('ajax', 'json')))
		){
			// Whatever filtering permanent state changed, the pagination returns to the first page
			if ($resetPage && !empty($new_state) && ($cur_state != $new_state))
			{
				$this->setState('list.start', 0);
				$app->setUserState($this->context . '.list.start', 0);
			}

			// Save the new value only if it is set in this request.
			if ($new_state !== null)
				$app->setUserState($key, $new_state);
			else
				$new_state = $cur_state;
		}

		return $new_state;
	}

	/**
	* Method to initialize a Many to One relation.
	*
	* @access	protected
	* @param	string	$name	Relation name.
	* @param	string	$foreignModelClass	Foreign model. Use the name when null.
	* @param	string	$localKey	The local key.
	* @param	string	$foreignKey	The foreign key.
	* @param	array	$selectFields	The required fields to include in select.
	* @param	boolean	$raw	Uses a raw implementation. More optimized, but choose false for using the model features.
	*
	* @return	void
	*/
	protected function hasMany($name, $foreignModelClass = null, $localKey = null, $foreignKey = null, $selectFields = array(), $raw = false)
	{
		$relation = $this->newRelation('hasMany', $name, $foreignModelClass, $selectFields);

		$relation->localKey = $localKey;
		$relation->foreignKey = $foreignKey;
		$relation->foreignPkey = 'id';

		$relation->raw = $raw;
	}

	/**
	* Method to initialize a Foreign Key relation.
	*
	* @access	protected
	* @param	string	$name	Relation name.
	* @param	string	$foreignModelClass	Foreign model. Use the name when null.
	* @param	string	$localKey	The local key.
	* @param	string	$foreignKey	The foreign key.
	* @param	array	$selectFields	The required fields to include in select.
	* @param	boolean	$raw	Uses a raw implementation. More optimized, but choose false for using the model features.
	*
	* @return	void
	*/
	protected function hasOne($name, $foreignModelClass = null, $localKey = null, $foreignKey = null, $selectFields = array(), $raw = false)
	{
		$relation = $this->newRelation('hasOne', $name, $foreignModelClass, $selectFields);

		$relation->localKey = $localKey;
		$relation->foreignKey = $foreignKey;
		$relation->foreignPkey = 'id';

		$relation->raw = $raw;
	}

	/**
	* Method to load related items.
	*
	* @access	public
	* @param	string	$name	Relation name.
	* @param	array	&$items	Local items to populate.
	* @param	array	$orm	ORM description for sub query, or cascad loading.
	*
	*
	* @since	3.1
	*
	* @return	void
	*/
	public function loadRelation($name, &$items, $orm = array())
	{
		if (empty($items))
			return;

		$relation = $this->getRelation($name);

		if (!$relation)
			return;

		$localKey = $relation->localKey;
		$foreignKey = $relation->foreignKey;


		$db = JFactory::getDbo();

		// Index all the items ID's
		$itemIds = array();
		foreach($items as $item)
			$itemIds[(int)$item->$localKey] = true;


		$model = ContactpushClassModelOrm::getModel($relation->foreignModelClass);
		if (!$model)
			return;

		// Empty context per default (for optimization)
		$context = '';
		if (array_key_exists('context', $orm))
			$context = $orm['context'];


		$model->setState('context', $context);

		// Apply the requested ORM
		// Recursive infinite sub requests
		if (!empty($orm))
			$model->orm($orm);


		$model->orm(array(

			// Disable the pagination
			'pagination' => array(
				'limit' => null,
				'start' => null
			)
		));

		$relType = 'array';
		switch($relation->type)
		{
			// Many to Many
			case 'belongsToMany':

				$pivotTable = $relation->pivotTable;
				$pivotForeignKey = $relation->pivotForeignKey;
				$pivotLocalKey = $relation->pivotLocalKey;


				$model->addJoin($db->qn($pivotTable)
					. ' AS ' . $db->qn('pivot')
					. ' ON a.' . $foreignKey . ' = pivot.' . $pivotForeignKey, 'LEFT');


				$model->addSelect('pivot.' . $pivotLocalKey . ' AS _local');


				// Filter mixing all parents to optimize the SQL queries
				$model->addWhere('pivot.' . $pivotLocalKey  . ' IN (' . implode(',', array_keys($itemIds)). ')');

				break;


			// Many to One
			case 'hasMany':

				$model->addSelect('a.' . $foreignKey);


				// Filter on the origin, mixing all parents to optimize the SQL queries
				$model->addWhere($db->qn($foreignKey) . ' IN (' . implode(',', array_keys($itemIds)). ')');

				break;

			// Foreign Key
			case 'hasOne':

				$relType = 'object';

				$model->addSelect('a.' . $foreignKey);

				// Filter on the origin, mixing all parents to optimize the SQL queries
				$model->addWhere($db->quoteName($foreignKey) . ' IN (' . implode(',', array_keys($itemIds)). ')');

				break;
		}


		// Get a mixed list
		$rows = $model->getItems();

		// Sort by $remoteValue
		$dico = array();
		foreach($rows as $row)
		{
			// N:M
			if (isset($row->_local))
			{
				$remoteValue = $row->_local;
				unset($row->_local);
			}

			// N:1
			else
			{
				$remoteValue = $row->$foreignKey;
			}

			if ($relType == 'array')
			{
				if (empty($dico[$remoteValue]))
					$dico[$remoteValue] = array();

				$dico[$remoteValue][] = $row;
			}
			else
				$dico[$remoteValue] = $row;
		}


		// Reassemble the lists in the correct parents
		foreach($items as $item)
		{
			$localValue = $item->$localKey;
			$item->$name = (isset($dico[$localValue])?$dico[$localValue]:(($relType == 'array')?array():null));
		}

		return $items;
	}

	/**
	* Method to load related items.
	*
	* @access	public
	* @param	string	$name	Relation name. Can be namespaced to load in cascad
	*
	* @return	void
	*/
	public function loadRelations($name)
	{
		// Cascad chaining
		$parts = explode('.', $name);
		$chain = null;
		if (count($parts) > 1)
		{
			$name = $parts[0];
			array_shift($parts);
			$chain = implode('.', $parts);
		}

		$items = $this->getItems();

		if (empty($items))
			return;


		$relation = $this->getRelation($name);

		if (!$relation)
			return;

		$localKey = $relation->localKey;
		$foreignKey = $relation->foreignKey;


		$db = JFactory::getDbo();

		$itemIds = array();
		foreach($items as $item)
		{
			$itemIds[(int)$item->$localKey] = true;
		}

		$modelName = $relation->foreignModelClass;
		$extension = 'Contactpush';
		$parts = explode('.', $modelName);
		if (count($parts) > 1)
		{
			$extension = $parts[0];
			$modelName = $parts[1];
		}


		$model = CkJModel::getInstance($modelName, ucfirst($extension) . 'Model');
		if (!$model)
			return;

		$model->setState('context', ''); // Empty select
		$model->setState('list.limit', null);
		$model->setState('list.start', null);

		$relType = 'array';

		switch($relation->type)
		{
			// Many to Many
			case 'belongsToMany':

				$pivotTable = $relation->pivotTable;
				$pivotForeignKey = $relation->pivotForeignKey;
				$pivotLocalKey = $relation->pivotLocalKey;


				$model->addJoin($db->qn($pivotTable)
					. ' AS ' . $db->qn('pivot')
					. ' ON a.' . $foreignKey . ' = pivot.' . $pivotForeignKey, 'LEFT');


				$model->addSelect('pivot.' . $pivotLocalKey . ' AS _local');


				// Filter mixing all parents to optimize the SQL queries
				$model->addWhere('pivot.' . $pivotLocalKey  . ' IN (' . implode(',', array_keys($itemIds)). ')');

				break;


			// Many to One
			case 'hasMany':

				$model->addSelect('a.' . $foreignKey);


				// Filter on the origin, mixing all parents to optimize the SQL queries
				$model->addWhere($db->qn($foreignKey) . ' IN (' . implode(',', array_keys($itemIds)). ')');

				break;

			// Foreign Key
			case 'hasOne':

				$relType = 'object';

				$model->addSelect('a.' . $foreignKey);

				// Filter on the origin, mixing all parents to optimize the SQL queries
				$model->addWhere($db->quoteName($foreignKey) . ' IN (' . implode(',', array_keys($itemIds)). ')');

				break;
		}


		// Select the given fields
		foreach($relation->selectFields as $selectField)
		{
			$model->addSelect('a.' . $selectField);
		}

		// Get a mixed list
		$rows = $model->getItems();

		// Load the sub items
		if ($chain)
			$model->loadRelations($chain);


		// Sort by $remoteValue
		$dico = array();
		foreach($rows as $row)
		{
			// N:M
			if (isset($row->_local))
			{
				$remoteValue = $row->_local;
				unset($row->_local);
			}

			// N:1
			else
			{
				$remoteValue = $row->$foreignKey;
			}

			if ($relType == 'array')
			{
				if (empty($dico[$remoteValue]))
					$dico[$remoteValue] = array();

				$dico[$remoteValue][] = $row;
			}
			else
				$dico[$remoteValue] = $row;
		}


		// Reassemble the lists in the correct parents
		foreach($items as $item)
		{
			$localValue = $item->$localKey;
			$item->$name = (isset($dico[$localValue])?$dico[$localValue]:(($relType == 'array')?array():null));
		}

		return $items;
	}

	/**
	* Load a N:x relation list to objects array in the item.
	*
	* @access	public
	* @param	object	&$items	The items to populate.
	* @param	string	$objectField	The item property name used for this list.
	* @param	string	$xrefTable	Cross Reference (Xref) table handling this link.
	* @param	string	$on	The FK fieldname from Xref pointing to the origin
	* @param	string	$key	The ID fieldname from Origin.
	* @param	array	$states	Cascad states followers, for recursive objects.
	* @param	string	$context	SQL predefined query
	*
	*
	* @since	Cook 2.6.3
	*
	* @return	void
	*/
	public function loadXref(&$items, $objectField, $xrefTable, $on, $key, $states = array(), $context = 'object.default')
	{
		$db = JFactory::getDbo();

		if ($this->getState('xref.' . $objectField) && count($items))
		{
			$itemIds = array();
			foreach($items as $item)
			{
				$itemIds[(int)$item->$key] = true;
			}

			$model = CkJModel::getInstance($xrefTable, 'ContactpushModel');

			// Prepare the fields to load, trough a context profile
			$model->setState('context', $context);

			// Be sure the 'on' field is in the query
			$model->addSelect('a.' . $on);

			// Filter on the origin, mixing all parents to optimize the SQL queries
			$model->addWhere($db->quoteName($on) . ' IN (' . implode(',', array_keys($itemIds)). ')');


			//Cascad objects states
			// Apply the namespaced states to the relative base namespace
			if (count($states))
			foreach($states as $state)
			{
				if ($val = $this->getState('xref.' . $objectField . '.' . $state))
					$model->setState('xref.' . $state, $val);
			}


			// Get a mixed list
			$rows = $model->getItems();

			// Sort by 'ON' field value
			$dico = array();
			foreach($rows as $row)
			{
				if (empty($dico[$row->$on]))
					$dico[$row->$on] = array();

				$dico[$row->$on][] = $row;

			}

			// Reassemble the lists in the correct parents
			foreach($items as $item)
			{
				$item->$objectField = $dico[$item->$key];
			}

		}
	}

	/**
	* Common method to prepare a new relation.
	*
	* @access	protected
	* @param	string	$type	Relation type.
	* @param	string	$name	Relation name.
	* @param	string	$foreignModelClass	Foreign model. Use the name when null.
	* @param	array	$selectFields	The required fields to include in select.
	*
	* @return	void
	*/
	protected function newRelation($type, $name, $foreignModelClass = null, $selectFields = array())
	{
		$relation = new stdClass();
		$extension = self::$extension;

		$relation->type = $type;

		if (!$foreignModelClass)
			$foreignModelClass = $name;

		$model = $foreignModelClass;
		$parts = explode('.', $foreignModelClass);
		if (count($parts) > 1)
		{
			$extension = $parts[0];
			$model = $parts[1];
		}

		$relation->extension = $extension;

		$relation->model = $model;

		$relation->name = $name;
		$relation->foreignModelClass = $extension . '.' . $model;
		$relation->foreignTable = '#__' . ($extension?strtolower($extension) . '_':'') . $relation->model;

		$relation->selectFields = $selectFields;

		// Index localy in the model
		$this->relations[$name] = $relation;

		return $relation;
	}

	/**
	* Request the ORM system.
	*
	* @access	public
	* @param	array	$description	Configuration or the ORM request
	*
	*
	* @since	Cook 3.0.10
	*
	* @return	void
	*/
	public function orm($description)
	{
		$this->orm->set($description);
	}

	/**
	* ORM Predefined profile for ALL columns and ALL rows.
	*
	* @access	protected
	*
	*
	* @since	3.1
	*
	* @return	void
	*/
	protected function ormAll()
	{
		$this->orm(array(
			'select' => '*',

			'pagination' => array(
				'limit' => null,
				'start' => null,
			)
		));
	}

	/**
	* Prepare some additional derivated objects.
	*
	* @access	public
	* @param	array	&$items	The objects to populate.
	*
	* @return	void
	*/
	public function populateObjects(&$items)
	{
		// Populate related lists (N:m)
		if (count($items))
		foreach($this->getRelations() as $name => $relation)
		{
			// Only N:m and N:1
			if (!in_array($relation->type, array('belongsToMany', 'hasMany')))
				continue;

			// Search for the ORM state vars
			if ($orm = $this->getState('relation.' . $name))
				$this->loadRelation($name, $items, $orm);
		}
	}

	/**
	* Prepare some additional important values.
	*
	* @access	public
	* @param	array	&$items	The objects to populate.
	*
	* @return	void
	*/
	public function populateParams(&$items)
	{
		if (!isset($items) || empty($items))
			return;

		$model = CkJModel::getInstance($this->view_item, 'ContactpushModel');
		foreach ($items as &$item)
		{
			// TODO : attribs
		//			$itemParams = new JRegistry;
		//			$itemParams->loadString((isset($item->attribs)?$item->attribs:$item->params));

			//$item->params = clone $this->getState('params');

			$item->params = new JObject();;

			if ($model)
			{
				if ($model->canView($item))
					$item->params->set('access-view', true);

				if ($model->canEdit($item))
					$item->params->set('access-edit', true);

				if ($model->canDelete($item))
					$item->params->set('access-delete', true);

				if ($model->isCheckedIn($item))
					$item->params->set('tag-checkedout', true);

				if (isset($item->published))
					$item->params->set('tag-published', $item->published);

				if (isset($item->default))
					$item->params->set('tag-default', $item->default);

			}
		}
	}

	/**
	* Method to auto-populate the model state.
	*
	* @access	protected
	* @param	string	$ordering	
	* @param	string	$direction	
	*
	* @return	void
	*/
	protected function populateState($ordering = null, $direction = null)
	{
		$jinput = JFactory::getApplication()->input;
		$layout = $jinput->get('layout', null, 'CMD');
		$render = $jinput->get('render', '', 'CMD');

		if ($layout == 'ajax')
		{
			$this->setState('context', 'ajax' . ($render?'.'.$render:''));
			$this->setState('list.limit', 0);
			$this->setState('list.start', 0);
		}


		$globalParams = JComponentHelper::getParams('com_contactpush', true);
		$this->setState('params', $globalParams);

		// If the context is set, assume that stateful lists are used.
		if ($this->context)
		{
			$app = JFactory::getApplication();

			// Handle legacy limitstart
			if ($jinput->get('limitstart') !== null)
				$jinput->set('start', $jinput->get('limitstart'));

		// FILTERS
			foreach($this->filter_vars as $var => $varType)
			{
				//1. Read the state var sent by the caller
				//2. Then read the Request in URL
				//3. Finally read the persistant value for THIS context
				$value = $this->state->get('filter.' . $var,
					$this->getUserStateFromRequest(
						$this->context . '.filter.' . $var,
						'filter_' . $var,
						null,
						$varType
					)
				);

				//Convert datetime entries back from a custom format
				if ($value && (preg_match("/^date:(.+)/", $varType, $matches)))
				{
					$date = ContactpushHelperDates::timeFromFormat($value, $matches[1]);
					if ($date)
					{
						jimport('joomla.utilities.date');
						$jdate = new JDate($date);
						$value = $jdate->toSql();
					}
					else
						continue;
				}
				$this->setState('filter.' . $var, $value);
			}

		// FILTERS : SEARCHES
			foreach($this->search_vars as $var => $varType)
			{
				//1. Read the state var sent by the caller
				//2. Then read the Request in URL
				//3. Finally read the persistant value for THIS context
				$value = $this->state->get('search.' . $var,
					$this->getUserStateFromRequest($this->context . '.search.' . $var,
						'search_' . $var,
						null,
						$varType
					)
				);

				$this->setState('search.' . $var, $value);
			}


		// PAGINATION : LIMIT
			//1. First read the state var sent by the caller
			//2. Then read the Request in URL
			//3. Then read the default limit value for THIS context
			//4. Finally read the list limit value from the Joomla configuration
			$limit = $this->state->get('list.limit',
				$app->getUserStateFromRequest($this->context . '.list.limit',
					'limit',
					$this->state->get('list.limit.default',
						$app->getCfg('list_limit')
					)
				)
			);

			$this->setState('list.limit', $limit);


		// PAGINATION : LIMIT START
			//1. First read the Request in URL
			//2. Then read the state var sent by the caller
			$value = $app->getUserStateFromRequest($this->context . '.start',
				'start',
				$this->state->get('list.start')
			);


			$limitstart = ($limit != 0 ? (floor($value / $limit) * $limit) : 0);
			$this->setState('list.start', $limitstart);


		// SORTING : ORDERING (Vocabulary confusion in Joomla. This is a SORTING. Ordering is an index value in the item.)
			//1. First read the Request in URL
			//2. Then read the default sorting value sent trough the args (called 'ordering')
			$value = $app->getUserStateFromRequest($this->context . '.list.ordering',
				'filter_order',
				$ordering
			);


			if (!in_array($value, $this->filter_fields))
			{
				$value = $ordering;
				$app->setUserState($this->context . '.ordercol', $value);
			}
			$this->setState('list.ordering', $value);


		// SORTING : DIRECTION
			//1. First read the Request in URL
			//2. Then read the default direction value sent trough the args.
			$value = $app->getUserStateFromRequest($this->context . '.orderdirn',
				'filter_order_Dir',
				$direction
			);

			if (!in_array(strtoupper($value), array('ASC', 'DESC', '')))
			{
				$value = $direction;
				$app->setUserState($this->context . '.orderdirn', $value);
			}
			$this->setState('list.direction', $value);
		}
		else
		{
			$this->setState('list.start', 0);
			$this->state->set('list.limit', 0);
		}

		if (defined('JDEBUG'))
			$_SESSION["Contactpush"]["Model"][$this->getName()]["State"] = $this->state;
	}

	/**
	* Register the ORM rules into model state SQL statements.
	*
	* @access	protected
	*
	*
	* @since	3.1
	*
	* @return	void
	*/
	protected function populateStatesOrm()
	{
		// Primary Key is always required
		$this->orm->select($this->getNamePk());

		// Load the SQL context profile (from ormXxxxYyyy() function)
		if ($context = $this->getState('context', 'all'))
		{
			$fctOrm = 'orm';
			foreach(explode('.', $context) as $part)
				$fctOrm .= ucfirst($part);


			// Call the predefined profile function
			if (method_exists($this, $fctOrm))
				$this->$fctOrm();
		}

		// Get the ORM description from the XML filters file
		$ormFilters = $this->getOrmFilters();

		// Request with ORM
		$this->orm($ormFilters);

		// Set the list ordering
		$orderCol = $this->getState('list.ordering');
		$orderDir = $this->getState('list.direction', 'ASC');


		if ($orderCol)
			$this->orm->order(array($orderCol => $orderDir));
	}

	/**
	* Method to easily filter the dates.
	*
	* @access	public
	* @param	string	$field	Field to apply the filter.
	* @param	string	$range	String to describe the starting time range, or predefined range. ex: [-4 day][-2 month][null][defined]
	* @param	string	$rangeEnd	String to describe the ending time range
	*
	* @return	void
	*/
	public function prepareFilterTime($field, $range, $rangeEnd = null)
	{
		$db = JFactory::getDbo();

		// Get UTC for now.
		$dNow = new JDate;
		$dBegin = clone $dNow;
		$dEnd = clone $dNow;

		// Define the starting time.
		switch($range)
		{
	
			case 'now':
				// 1 hour back per default.
				$dBegin->modify('-1 hour');
				break;
		
			case 'today':
				//Align on the days bounds
		
				// Ranges that need to align with local 'days' need special treatment.
				$app	= JFactory::getApplication();
				$offset	= $app->getCfg('offset');

				// Reset the start time to be the beginning of today, local time.
				$dBegin	= new JDate('now', $offset);
				$dBegin->setTime(0, 0, 0);

				// Now change the timezone back to UTC.
				$tz = new DateTimeZone('GMT');
				$dBegin->setTimezone($tz);
				break;
	
			default: 		
				$dBegin->modify($range);
			break;
		}


		//Define the ending time.
		switch($rangeEnd)
		{
			case null: break;

	
			default: 		
				$dEnd->modify($rangeEnd);
			break;
		}

		// Search for null dates.
		if ($range == 'null')
		{
			$this->addWhere($field . " IS NULL ");
			return;
		}

		// Search for defined dates.
		if ($range == 'defined')
		{
			$this->addWhere($field . " <> NULL ");
			return;
		}

		// Time cannot be null.
		$this->addWhere($field . " IS NOT NULL ");

		// Apply the STARTING time filter.
		$this->addWhere($field . " >= " . $db->quote($dBegin->toSql()));			

		// Apply the ENDING time filter.
		$this->addWhere($field . " < " . $db->quote($dEnd->toSql()));			
	}

	/**
	* Prepare the query for filtering accesses. Can be used on foreign keys.
	*
	* @access	protected
	* @param	varchar	$table	The table alias (_tablealias_).
	* @param	varchar	&$whereAccess	The returned SQL access filter. Set to true to activate it.
	* @param	varchar	&$wherePublished	The returned SQL published filter. Set to true to activate it.
	* @param	varchar	&$allowAuthor	The returned SQL to allow author to pass. Set to true to activate it.
	*
	* @return	void
	*/
	protected function prepareQueryAccess($table = 'a', &$whereAccess = null, &$wherePublished = null, &$allowAuthor = null)
	{
		$acl = ContactpushHelper::getActions();

		// Must be aliased ex : _tablename_
		if ($table != 'a')
			$table = '_' . trim($table, '_') . '_';


		// ACCESS - View Level Access
		if ($whereAccess)
		{
			// Select fields requirements
			if ($table != 'a')
				$this->addSelect($table . '.access AS `' . $table . 'access`');	

			$whereAccess = '1';
			if (!$this->canAdmin())
			{	
			    $groups	= implode(',', JFactory::getUser()->getAuthorisedViewLevels());
				$whereAccess = $table . '.access IN ('.$groups.')';
			}
		}

		// ACCESS - Author
		if ($allowAuthor)
		{
			// Select fields requirements
			if ($table != 'a')
				$this->addSelect($table . '.created_by AS `' . $table . 'created_by`');

			$allowAuthor = '0';
			//Allow the author to see its own unpublished/archived/trashed items
			if ($acl->get('core.edit.own') || $acl->get('core.view.own') || $acl->get('core.delete.own'))
				$allowAuthor = $table . '.created_by = ' . (int)JFactory::getUser()->get('id');
		
		}

		// ACCESS - Publish state
		if ($wherePublished)
		{
			// Select fields requirements
			if ($table != 'a')
				$this->addSelect($table . '.published AS `' . $table . 'published`');

			$wherePublished = '(' . $table . '.published = 1 OR ' . $table . '.published IS NULL)'; //Published or undefined state
			//Allow some users to access (core.edit.state)
			if ($acl->get('core.edit.state'))
				$wherePublished = '1'; //Do not filter

			// FILTER - Published state
			$published = $this->getState('filter.published');

			//Only apply filter on current table. Aand only if ACL permits.
			if (($table == 'a') && (is_numeric($published)) && $acl->get('core.edit.state'))
			{
				//Limit to publish state when filter is applied
				$wherePublished = $table . '.published = ' . (int)$published;
				//Does not apply the author condition when filter is defined
				$allowAuthor = '0';
			}
		}

		// Fallback values
		if (!$whereAccess)
			$whereAccess = '1';

		if (!$allowAuthor)
			$allowAuthor = '0';

		if (!$wherePublished)
			$wherePublished = '1';
	}

	/**
	* This feature is the blueprint of ORM-kind feature. It create the optimized
	* SQL query for mounting an object, including foreign links.
	*
	* @access	public
	* @param	array	$headers	The header structure. see:https://www.akeebabackup.com/documentation/fof/common-fields-for-all-types.html
	*
	*
	* @since	Cook 2.6.3
	*
	* @return	void
	*/
	public function prepareQueryHeaders($headers)
	{
		if (!count($headers))
			return;

		$db = JFactory::getDbo();

		foreach($headers as $namespace => $header)
		{
			// the namespace is used to localize the foreign key path
			$fieldAlias = $namespace = $header['name'];
			if (isset($header['namespace']))
				$namespace = $header['namespace'];

			$parts = explode('.' ,$namespace);
			$isFk = (count($parts) > 1);


			// Physical field name is always the last part
			$fieldname = $parts[count($parts)-1];
			$current = $parts[0];

			$parentTable = 'a';

			for($i = 0 ; $i < (count($parts)) ; $i++)
			{
				$isLast = ($i == (count($parts) - 1));
				$current = $parts[$i];

				// Select the field
				if ($isLast)
					break;

				$tableName = self::fkTable($current);
				$tableAlias = '_' . $current . '_';

				// Join the required tables
				$this->addJoin($db->qn($tableName)
					.	' AS ' . $tableAlias
					.	' ON ' . $tableAlias . '.id'
					.	' = ' . $parentTable . '.' . $current

					, 'LEFT');

				$parentTable = $tableAlias;
			}

			// Instance the field in query
			$this->addSelect($parentTable .'.'. $current . ' AS ' . $db->qn($fieldAlias));
		}
	}

	/**
	* Join the table behind the Foreign Key.
	*
	* @access	protected
	* @param	string	$namespace	Namespace of the Foreign Keys chain
	*
	*
	* @since	Cook 3.0.10
	*
	* @return	void
	*/
	protected function prepareQueryJoin($namespace)
	{
		$parts = explode('.', $namespace);
		$model = $this;
		$table = $this->getName();

		$tableAlias = 'a';
		foreach($parts as $field)
		{
			$relation = $model->getRelation($field);

			$this->addJoin('`' . $relation->foreignTable . '` AS _' . $field
				. '_ ON _' . $field . '_.' . $relation->foreignKey . ' = ' . $tableAlias . '.' . $field, 'LEFT');


			$tableAlias = '_' . $field . '_';

			$partsModel = explode('.', $relation->foreignModelClass);

			$model = CkJModel::getInstance($partsModel[1], ucfirst($partsModel[0]) . 'Model');
		}
	}

	/**
	* Method to prepare a query including relations.
	*
	* @access	public
	* @param	string	$name	Relation name. Can be namespaced to load in cascad
	* @param	boolean	$groupOrder	Instance a prioritary order for groups.
	*
	* @return	void
	*/
	public function prepareQueryRelations($name, $groupOrder = false)
	{
		if (empty($name))
			return;

		$db = JFactory::getDbo();

		$extension = self::$extension;

		// Nested relations
		$parts = explode('.', $name);


		// Initialize the base
		$parentTable = 'a';
		$fieldAliasBase = '';
		$model = $this;

		for($i = 0 ; $i < count($parts) ; $i++)
		{
			$name = $parts[$i];

			$relation = $model->getRelation($name);

			// Break whole process here (cannot cascad more)
			if (!$relation)
				return;

			// Only Foreign Key relation are handled here
			if ($relation->type != 'hasOne')
				return;

			$foreignTable = $relation->foreignTable;

			$fieldAliasBase .= '_' . $relation->localKey;


			// Join the required tables
			$this->addJoin($db->quoteName($foreignTable)
				.	' ON ' . $foreignTable . '.' . $relation->foreignKey
				.	' = ' . $parentTable . '.' . $relation->localKey

				, 'LEFT');

			// Select the foreign Pkey
			$this->addSelect($foreignTable .'.'. $relation->foreignPkey . ' AS ' . $db->quoteName($fieldAliasBase));


			$defaultField = null;

			// Instance every select field in query
			foreach($relation->selectFields as $selectField)
			{
				if (!$defaultField)
					$defaultField = $selectField;

				$fieldAlias = $fieldAliasBase . '_' . $selectField;
				$this->addSelect($foreignTable .'.'. $selectField . ' AS ' . $db->quoteName($fieldAlias));
			}


			// Instance a prioritary order, when the relations are used to group a list
			if ($groupOrder && $defaultField)
				$this->addGroupOrder($foreignTable .'.'. $defaultField . ' ASC');


			// Initialize for the next nested relation
			if ($i < (count($parts)))
			{
				$parentTable = $foreignTable;
				$model = CkJModel::getInstance($relation->model, ucfirst($relation->extension) . 'Model');
			}

		}
	}

	/**
	* Prepare the language translation of items for SQL query.
	*
	* @access	protected
	* @param	array	$fields	The fields you want to translate.
	* @param	array	$options	An array of configuration.
	*
	* @return	void
	*/
	protected function prepareQueryTranslate($fields, $options = array())
	{
		if (empty($fields))
			return;

		//Define an alias prefix when the selected field is abroad FK. (ie: _product_category_title, use : _product_category_)
		$fieldPrefix = '';
		if (isset($options['fieldPrefix']))
		{
			$fieldPrefix = $options['fieldPrefix'];
			$tableAlias = $fieldPrefix;
			$langTableAlias = '__lang' . $fieldPrefix;
		}
		else
		{
			$tableAlias = 'a';
			$langTableAlias = '__lang_';
		}

		//The alias used in query for temporary load the related language item (be careful unicity of table aliases)
		if (isset($options['langTableAlias']))
			$langTableAlias = $options['langTableAlias'];

		//The table name from witch are stored the languages strings
		$tableFrom = '#__' . ltrim($this->option, '_com') . '_' . $this->getName();
		if (isset($options['tableFrom']))
			$tableFrom = $options['tableFrom'];

		//Define the field on which the filter is working. Language tag (ie: en-GB).
		$keyLang = 'language';
		if (isset($options['keyLang']))
			$keyLang = $options['keyLang'];

		//Define the recursive field FK which relate to the original item
		$keyXref = 'xref';
		if (isset($options['keyXref']))
			$keyXref = $options['keyXref'];

		//Limit to the root elements when not the root table (a.)
		if (isset($options['tableFrom']))
			$this->addWhere("($tableAlias.$keyXref IS NULL || $tableAlias.$keyXref = 0)");

		//Apply the filter
		$stateValue = $this->getState('filter.language');
		if ($stateValue !== null)
		{
			// Join language table (recursive 1 level)
			$this->addJoin("`$tableFrom` AS `$langTableAlias` ON ($langTableAlias.$keyXref = $tableAlias.id AND $langTableAlias.$keyLang = "
					. $this->_db->Quote($stateValue)
					. ')' , 'LEFT');

			//Translatable fields
			foreach($fields as $key)
				$this->addSelect("(CASE WHEN ($langTableAlias.$key IS NOT NULL AND $langTableAlias.$key > 0) THEN $langTableAlias.$key ELSE $tableAlias.$key END) AS `$fieldPrefix$key`");
		}
	}

	/**
	* Proxy. Dump the query to the screen.
	*
	* @access	public
	* @param	JDatabaseQuery	$query	Query to dump.
	* @param	boolean	$output	Print the query description to the standard output.
	*
	*
	* @since	3.1
	*
	* @return	array	Query description.
	*/
	public function queryDump($query, $output = true)
	{
		return ContactpushClassModelOrm::queryDump($query, $output);
	}

	/**
	* Method to adjust the ordering of a row.
	*
	* @access	public
	* @param	array	$ids	The ID of the primary key to move.
	* @param	int	$inc	Delta increment, usually +1 or -1.
	*
	*
	* @since	11.1
	*
	* @return	boolean	True on success
	*/
	public function reorder($ids, $inc)
	{
		$model = $this->getModel(true);

		$table = $model->getTable();
		$table->load($ids[0]);

		if (!$table->move($inc))
			return false;

		$conditions = $model->getReorderConditions($table);
		$conditions = (count($conditions)?implode(" AND ", $conditions):'');
		$table->reorder($conditions);

		return true;
	}

	/**
	* Unify the orderdering statements syntax.
	*
	* @access	protected
	* @param	string	$order	SQL order statement
	*
	*
	* @since	3.1
	*
	* @return	void
	*/
	protected function sanitizeOrdering($order)
	{
		$parts = explode(' ', trim($order));

		$field = $parts[0];

		$dir = 'ASC'; // Per default
		if ((count($parts) > 1) && in_array($parts[1], array('ASC', 'DESC')))
			$dir = $parts[1];

		return $field . ' ' . $dir;
	}

	/**
	* Saves the manually set order of records.
	*
	* @access	public
	* @param	array	$pks	An array of primary key ids.
	* @param	array	$order	order values
	*
	*
	* @since	11.1
	*
	* @return	boolean	True on success
	*/
	public function saveorder($pks, $order)
	{
		$model = $this->getModel(true);
		$model->saveorder($pks, $order);
	}

	/**
	* Method to set model state variables. Update local vars.
	*
	* @access	public
	* @param	string	$property	The name of the property.
	* @param	mixed	$value	The value of the property to set or null.
	*
	*
	* @since	11.1
	*
	* @return	mixed	The previous value of the property or null if not set.
	*/
	public function setState($property, $value = null)
	{
		if ($property == 'context')
			$this->context = $value;
	
		return parent::setState($property, $value);
	}

	/**
	* Construct the field alias based on the table alias.
	*
	* @access	protected
	* @param	string	$namespace	Namespace of the Foreign Keys chain
	*
	*
	* @since	Cook 3.0.10
	*
	* @return	void
	*/
	protected function tableFieldAlias($namespace)
	{
		$parts = explode('.', $namespace);

		$table = 'a';

		foreach($parts as $part)
		{
			$alias = $table . '.' . $part;
			$table = '_' . $part . '_';
		}

		return $alias;
	}

	/**
	* Synchronize the N:M references Add/Remove.
	*
	* @access	public
	* @param	string	$field	Fk fieldname in the Xref table
	* @param	array	$values	Array of ID of the values for $field
	* @param	string	$on	Fk fieldname pointing the origin referral.
	* @param	integer	$id	ID value of the origin.
	*
	*
	* @since	Cook 2.6.3
	*
	* @return	boolean	True when success.
	*/
	public function updateXref($field, $values, $on, $id)
	{
		$db = JFactory::getDbo();

		$sqlValues = implode(',', $values);
		if (empty($sqlValues))
			$sqlValues = '0';


		// Get all current links in context
		$model = CkJModel::getInstance($this->getName(), 'ContactpushModel');
		$model->addWhere($db->quoteName($on) . '='. $id);

		$xref = $model->getItems();
		$refs = array();

		$isNm = true;
		if ($field == null)
		{
			$isNm = false;
			$field = 'id';
		}

		$delete = array();
		foreach($xref as $row)
		{
			$refs[] = $row->$field;
			if (!in_array($row->$field, $values))
			{
				//Delete row
				$delete[] = $row->id;
			}
		}

		$create = array();
		foreach($values as $val)
		{
			if (!in_array($val, $refs))
			{
				//Create new row
				$create[] = $val;
			}
		}

		$result = true;

		// In case on N:M, the links are physical rows
		if ($isNm)
		{
			//Apply delete
			$model = CkJModel::getInstance($this->view_item, 'ContactpushModel');
			if (count($delete))
				if (!$model->delete($delete))
					$result = false;


			// Create new entries
			$model = CkJModel::getInstance($this->view_item, 'ContactpushModel');
			if (count($create))
			foreach($create as $val)
			{
				if (!$model->save(array(
					'id' => 0, //New
					$on => $id,
					$field => $val
				)))
					$result = false;
			}
		}

		// In case of N:1, the links are FK from the opposite table
		else
		{

			if (count($delete))
			{
				$query = $db->getQuery(true);
				$query->update('#__contactpush_' . $this->getName())

					// Unlink it
					->set($db->quoteName($on) . '= NULL')

					// From the given list to delete
					->where($db->quoteName($field) . ' IN (' . implode(',', $delete). ')');

				$db->setQuery($query);


				if (!$db->query())
					$result = false;
			}

			if (count($create))
			{
				$query = $db->getQuery(true);
				$query->update('#__contactpush_' . $this->getName())

					// Link it
					->set($db->quoteName($on) . '='. (int)$id)

					// Facultative security : ONLY free items are linkables $on = (NULL or O)
					->where('(' . $db->quoteName($on) . ' IS NULL OR ' . $db->quoteName($on) . ' = 0 '. ')')

					// From the given list to create
					->where($db->quoteName($field) . ' IN (' . implode(',', $create). ')');

				$db->setQuery($query);

				if (!$db->query())
					$result = false;

			}
		}

		return $result;

	}


}

// Load the fork
ContactpushHelper::loadFork(__FILE__);

// Fallback if no fork has been found
if (!class_exists('ContactpushClassModelList')){ class ContactpushClassModelList extends ContactpushCkClassModelList{} }