Your IP : 216.73.216.240
<?php
/*
* @package Tech Fry Library
* @license https://www.gnu.org/licenses/gpl-3.0.en.html
*/
namespace TechFry\Library\Model\Back;
\defined('_JEXEC') or die;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\Database\ParameterType;
use Joomla\CMS\Date\Date;
use Joomla\Utilities\ArrayHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;
use TechFry\Library\TDb;
use TechFry\Library\Model\TfModelTrait;
class TfModelList extends ListModel
{
// ListModel: getActiveFilters(), getFilterForm(), getItems(), getPagination(), getTotal()
use TfModelTrait, TfFilterTrait;
public $select_fields = [];
public $left_joins = [];
public $inner_joins = [];
public $where = [];
public $orderCol = 'a.id';
public $orderDirn = 'DESC';
public $search = 'a.title'; // Default search field
public $search_fields = []; // key - operator, value - table.database_column
public $filters = []; // key - name in filter form, value - table.database_column
public $datefilters = [];
public $sorting_fields = [];
public $sortings = [];
public $is_ml = false;
public function __construct($config = [], ?MVCFactoryInterface $factory = null)
{
if ($this->table_name)
{
$db = new TDb($this->table_name);
$this->table_columns = $db->get_table_columns();
}
// Set default filters - published, access, note
$this->set_filters();
// Set default fields for sorting - id, title, published, created, modified, hits, ordering, access, note
$this->set_sorting_fields();
// Set default fields for search term - id, description, note
$this->set_search_fields();
$config['filter_fields'] = $this->sorting_fields;
if (Associations::isEnabled())
{
$config['filter_fields'][] = 'association';
}
parent::__construct($config, $factory);
$this->app = Factory::getApplication();
$this->input = $this->app->input;
$this->option = $this->input->get('option');
$this->cparams = ComponentHelper::getParams($this->option);
$this->today = Factory::getDate();
$this->date_sql = $this->today->toSql();
$this->date_unix = $this->today->toUnix();
$this->date_lc3 = $this->today->format(Text::_('DATE_FORMAT_LC3'));
$this->juser = $this->app->getIdentity();
$this->user_levels = $this->juser->getAuthorisedViewLevels();
$this->url = Uri::getInstance()->toString();
$this->post = $this->input->post->getArray();
$this->get = $this->input->get->getArray();
$this->set_sortings_array();
}
public function getFilterForm($data = [], $loadData = true)
{
$form = parent::getFilterForm($data, $loadData);
// Search
$element = '<field name="search" type="text" inputmode="search" label="COM_TF_FILTER_SEARCH_LABEL" hint="JSEARCH_FILTER" />';
$form = $this->add_field($form, $element, 'default', 'filter');
// Filters
foreach ($this->filters as $k => $v)
{
switch ($k)
{
case 'published' :
$element = $this->filter_published_field();
$form = $this->add_field($form, $element, 'default', 'filter');
break;
case 'access' :
$element = $this->filter_access_field();
$form = $this->add_field($form, $element, 'default', 'filter');
break;
case 'note' :
$element = $this->filter_note_field($this->table_name);
$form = $this->add_field($form, $element, 'default', 'filter');
break;
case 'language' :
$element = $this->filter_language_field();
$form = $this->add_field($form, $element, 'default', 'filter');
break;
}
}
// Ordering field
$element = $this->filter_ordering_field($this->orderCol);
$form = $this->add_field($form, $element, 'default', 'list');
$ordering = $form->getField('fullordering', 'list');
foreach ($this->sortings as $k => $v)
{
$ordering->addOption($k, array('value' => $v));
}
// Limit field
$element = $this->filter_limit_field();
$form = $this->add_field($form, $element, 'default', 'list');
$data = $this->loadFormData();
$form->bind($data);
return $form;
}
// Build an SQL query to load the list data
protected function getListQuery()
{
$char = 'a';
if (empty($this->select_fields))
{
$this->select_fields = array('a.*');
}
$db = $this->getDatabase();
$query = $db->getQuery(true);
$user = $this->getCurrentUser();
$query->select($this->select_fields)
->from($db->quoteName('#__' . $this->table_name, $char));
if (!empty($this->left_joins))
{
foreach ($this->left_joins as $join)
{
$char++;
$query->join('LEFT', $db->quoteName('#__' . $join['table'], $char), $db->quoteName($join['on1']) . ' = ' . $db->quoteName($join['on2']));
}
}
if (!empty($this->inner_joins))
{
foreach ($this->inner_joins as $join)
{
$char++;
$query->join('INNER', $db->quoteName('#__' . $join['table'], $char), $db->quoteName($join['on1']) . ' = ' . $db->quoteName($join['on2']));
}
}
// Add the list ordering clause
$orderCol = $this->state->get('list.ordering', $this->orderCol);
$orderDirn = $this->state->get('list.direction', $this->orderDirn);
$query->order($db->escape($orderCol) . ' ' . $db->escape($orderDirn));
// Join over users
if (\array_key_exists('created_by', $this->table_columns))
{
$query->select(array($db->quoteName('ua.name', 'author_name'), $db->quoteName('ua.email', 'author_email')));
$query->join('LEFT', $db->quoteName('#__users', 'ua'), $db->quoteName('ua.id') . ' = ' . $db->quoteName('a.created_by'));
}
// Join over access
if (\array_key_exists('access', $this->table_columns))
{
$query->select(array($db->quoteName('ag.title', 'access_level')));
$query->join('LEFT', $db->quoteName('#__viewlevels', 'ag'), $db->quoteName('ag.id') . ' = ' . $db->quoteName('a.access'));
}
// Join over languages
if (Associations::isEnabled() && $this->is_ml)
{
$query->select(array($db->quoteName('l.title', 'language_title'), $db->quoteName('l.image', 'language_image')));
$query->join('LEFT', $db->quoteName('#__languages', 'l'), $db->quoteName('l.lang_code') . ' = ' . $db->quoteName('a.language'));
}
// Join over associations
if (Associations::isEnabled() && $this->is_ml)
{
$subQuery = $db->getQuery(true)
->select('COUNT(' . $db->quoteName('asso1.id') . ') > 1')
->from($db->quoteName('#__associations', 'asso1'))
->join('INNER', $db->quoteName('#__associations', 'asso2'), $db->quoteName('asso1.key') . ' = ' . $db->quoteName('asso2.key'))
->where(array($db->quoteName('asso1.id') . ' = ' . $db->quoteName('a.id'), $db->quoteName('asso1.context') . ' = ' . $db->quote('com_tfcontent.item'))
);
$query->select('(' . $subQuery . ') AS ' . $db->quoteName('association'));
}
// Where Conditions
foreach ($this->where as $condition)
{
if (\is_array($condition))
{
$query->Where($db->quoteName($condition[0]) . ' ' . $condition[1] . ' ' . $db->quote($condition[2]));
}
else
{
$query->Where($condition);
}
}
// Add filters
$filters = $this->filters;
// Filter by search
$search = $this->getState('filter.search');
if (!empty($search))
{
$default_search = true;
foreach ($this->search_fields as $search_field => $db_field)
{
if (stripos($search, $search_field . ':') === 0)
{
$search = substr($search, strlen($search_field) + 1);
if ($search_field == 'id')
{
$query->where($db->quoteName($db_field) . ' = :search')
->bind(':search', $search, ParameterType::INTEGER);
}
else
{
$search = '%' . str_replace(' ', '%', trim($search)) . '%';
$query->where($db->quoteName($db_field) . ' LIKE :search')
->bind(':search', $search);
}
$default_search = false;
break;
}
}
if ($default_search)
{
$search = '%' . str_replace(' ', '%', trim($search)) . '%';
$query->where($db->quoteName($this->search) . ' LIKE :search')
->bind(':search', $search);
}
}
// Filter by access level
if (array_key_exists('access', $filters))
{
$access = $this->getState('filter.access');
if (is_numeric($access))
{
$access = (int) $access;
$query->where($db->quoteName('a.access') . ' = :access')
->bind(':access', $access, ParameterType::INTEGER);
}
elseif (\is_array($access))
{
$access = ArrayHelper::toInteger($access);
$query->whereIn($db->quoteName('a.access'), $access);
}
unset($filters['access']);
}
// Filter by published state
if (array_key_exists('published', $filters))
{
$published = (string) $this->getState('filter.published');
if ($published !== '*')
{
if (is_numeric($published))
{
$state = (int) $published;
$query->where($db->quoteName('a.published') . ' = :published')
->bind(':published', $state, ParameterType::INTEGER);
}
else
{
$query->whereIn($db->quoteName('a.published'), array(0, 1));
}
}
unset($filters['published']);
}
// Other Filters
foreach ($filters as $k => $v)
{
$value = $this->getState('filter.' . $k);
if ($value != '')
{
if (\is_array($value))
{
// To Do: Array may contain integers or strings
$value = ArrayHelper::toInteger($value);
$query->whereIn($db->quoteName($v), $value);
}
else
{
$query->where($db->quoteName($v) . ' = ' . $db->quote($value));
}
}
}
// Date Range Filters
foreach ($this->datefilters as $k => $v)
{
$range = $this->getState('filter.' . $k);
if ($range)
{
$dates = $this->build_date_range($range);
if ($dates['start'])
{
$query->where($db->quoteName($v) . ' > ' . $db->quote($dates['start']));
}
if ($dates['end']) // Assume start to be today
{
$query->where($db->quoteName($v) . ' < ' . $db->quote($dates['end']));
}
if ($dates['start'] === false && $dates['end'] === false)
{
$query->where($db->quoteName($v) . ' IS NULL');
}
}
}
if (Factory::getConfig()->get('debug'))
{
$this->get_message($query->dump(), 'info');
}
return $query;
}
protected function populateState($ordering = null, $direction = null)
{
// Called automatially on the first call to the getState() method (called by getItems())
// The getState() is defined in StateBehaviorTrait used by BaseModel
// list.direction, list.ordering, list.limit, list.start
// filter.search, filter.published, filter.note, etc.
$app = Factory::getApplication();
// Adjust the context to support modal layouts.
if ($layout = $app->input->get('layout'))
{
$this->context .= '.' . $layout;
}
// Adjust the context to support forced languages.
$forcedLanguage = $app->input->get('forcedLanguage', '', 'CMD');
if ($forcedLanguage)
{
$this->context .= '.' . $forcedLanguage;
}
parent::populateState($ordering, $direction);
// If there is a forced language, then define that filter for the query where clause
if (!empty($forcedLanguage))
{
$this->setState('filter.language', $forcedLanguage);
}
}
// Count total items in $table where $column is $value
public function count_items($table, $column, $value)
{
$db = Factory::getDbo();
$query = $db->getQuery(true);
$query->select('COUNT(*)')
->from($db->quoteName('#__' . $table))
->where($db->quoteName($column) . ' = ' . $db->quote($value));
$db->setQuery($query);
$total = $db->loadResult();
return $total;
}
// Add field to fieldset in the form
public function add_field($form, $element, $fieldset = 'default', $group = null, $replace = true)
{
$xml = new \SimpleXMLElement($element);
if ($form)
{
$form->setField($xml, $group, $fieldset, $replace);
}
return $form;
}
public function set_sortings_array()
{
foreach ($this->sorting_fields as $field)
{
switch ($field)
{
case 'a.title' :
$this->sortings['JGLOBAL_TITLE_ASC'] = 'a.title ASC';
$this->sortings['JGLOBAL_TITLE_DESC'] = 'a.title DESC';
break;
case 'a.published' :
$this->sortings['JSTATUS_ASC'] = 'a.published ASC';
$this->sortings['JSTATUS_DESC'] = 'a.published DESC';
break;
case 'a.id' :
$this->sortings['JGRID_HEADING_ID_ASC'] = 'a.id ASC';
$this->sortings['JGRID_HEADING_ID_DESC'] = 'a.id DESC';
break;
case 'a.created' :
$this->sortings['COM_TF_ORDERING_CREATED_ASC'] = 'a.created ASC';
$this->sortings['COM_TF_ORDERING_CREATED_DESC'] = 'a.created DESC';
break;
case 'a.modified' :
$this->sortings['COM_TF_ORDERING_MODIFIED_ASC'] = 'a.modified ASC';
$this->sortings['COM_TF_ORDERING_MODIFIED_DESC'] = 'a.modified DESC';
break;
case 'a.hits' :
$this->sortings['JGLOBAL_HITS_ASC'] = 'a.hits ASC';
$this->sortings['JGLOBAL_HITS_DESC'] = 'a.hits DESC';
break;
case 'a.ordering' :
$this->sortings['JGRID_HEADING_ORDERING_ASC'] = 'a.ordering ASC';
$this->sortings['JGRID_HEADING_ORDERING_DESC'] = 'a.ordering DESC';
break;
case 'a.access' :
$this->sortings['JGRID_HEADING_ACCESS_ASC'] = 'a.access ASC';
$this->sortings['JGRID_HEADING_ACCESS_DESC'] = 'a.access DESC';
break;
case 'a.note' :
$this->sortings['COM_TF_ORDERING_NOTE_ASC'] = 'a.note ASC';
$this->sortings['COM_TF_ORDERING_NOTE_DESC'] = 'a.note DESC';
break;
case 'a.language' :
$this->sortings['JGRID_HEADING_LANGUAGE_ASC'] = 'a.language ASC';
$this->sortings['JGRID_HEADING_LANGUAGE_DESC'] = 'a.language DESC';
break;
default :
$field_lang = (strpos($field, '.') == true) ? substr($field, 2) : $field;
$this->sortings[strtoupper($this->option) . '_ORDERING_' . strtoupper($field_lang) . '_ASC'] = $field . ' ASC';
$this->sortings[strtoupper($this->option) . '_ORDERING_' . strtoupper($field_lang) . '_DESC'] = $field . ' DESC';
}
}
}
public function set_filters()
{
$defaults = array('published', 'access', 'note');
// Key is the name of field in filter form; Value is column in db query
foreach ($defaults as $default)
{
if (array_key_exists($default, $this->table_columns))
{
$this->filters[$default] = 'a.' . $default;
}
}
}
public function set_sorting_fields()
{
$defaults = array('id', 'title', 'published', 'created', 'modified', 'hits', 'ordering', 'access', 'note');
foreach ($defaults as $default)
{
if (array_key_exists($default, $this->table_columns))
{
$this->sorting_fields[] = 'a.' . $default;
}
}
}
public function set_search_fields()
{
$defaults = array('id', 'description', 'note');
foreach ($defaults as $default)
{
if (array_key_exists($default, $this->table_columns))
{
$this->search_fields[$default] = 'a.' . $default;
}
}
}
private function build_date_range($range)
{
$start = new Date;
$end = new Date;
switch ($range)
{
case 'past_week':
$start->modify('-7 day');
break;
case 'past_1month':
$start->modify('-1 month');
break;
case 'past_3month':
$start->modify('-3 month');
break;
case 'past_6month':
$start->modify('-6 month');
break;
case 'past_year':
$start->modify('-1 year');
break;
case 'post_year':
$start = false;
$end->modify('-1 year');
break;
case 'next_week':
$end->modify('+7 day');
break;
case 'next_1month':
$end->modify('+1 month');
break;
case 'next_3month':
$end->modify('+3 month');
break;
case 'next_6month':
$end->modify('+6 month');
break;
case 'next_year':
$end->modify('+1 year');
break;
case 'post_future_year':
$end = false;
$start->modify('+1 year');
break;
case 'today':
// Ranges that need to align with local 'days' need special treatment.
$app = Factory::getApplication();
$offset = $app->get('offset');
// Reset the start time to be the beginning of today, local time.
$start = new Date('now', $offset);
$start->setTime(0, 0, 0);
// Now change the timezone back to UTC.
$tz = new \DateTimeZone('GMT');
$start->setTimezone($tz);
$end = false;
break;
case 'never':
$end = false;
$start = false;
break;
}
return ['end' => $end, 'start' => $start];
}
}