chevereto-free/app/lib/classes/class.listing.php

904 lines
34 KiB
PHP

<?php
/* --------------------------------------------------------------------
This file is part of Chevereto Free.
https://chevereto.com/free
(c) Rodolfo Berrios <rodolfo@chevereto.com>
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
--------------------------------------------------------------------- */
namespace CHV;
use G;
use Exception;
class Listing
{
protected static $valid_types = ['images', 'albums', 'users'];
protected static $valid_sort_types = ['date', 'size', 'views', 'id', 'image_count', 'name', 'title', 'username'];
// Set the type of list
public function setListing($listing)
{
$this->listing = $listing;
}
// Sets the type of resource being listed
public function setType($type)
{
$this->type = $type;
}
// Sets the offset (sql> LIMIT offset,limit)
public function setOffset($offset)
{
$this->offset = intval($offset);
}
public function setSeek($seek)
{
if (strpos($seek, '.') !== false) {
$explode = explode('.', $seek);
$explode[1] = decodeID($explode[1]);
$this->seek = $explode;
return;
}
$decodeID = decodeID($seek);
if (ctype_digit($decodeID)) {
$this->seek = $decodeID;
return;
}
$this->seek = $seek;
}
public function setReverse($bool)
{
$this->reverse = $bool;
}
public function setParamsHidden($params)
{
$this->params_hidden = $params;
}
// Sets the limit (sql> LIMIT offset,limit)
public function setLimit($limit)
{
$this->limit = intval($limit);
}
public function setItemsPerPage($count)
{
$this->items_per_page = intval($count);
}
// Sets the sort type (sql> SORT BY sort_type)
public function setSortType($sort_type)
{
$this->sort_type = $sort_type == 'date' ? 'id' : $sort_type;
}
// Sets the sort order (sql> DESC | ASC)
public function setSortOrder($sort_order)
{
$this->sort_order = $sort_order;
}
// Sets the WHERE clause
public function setWhere($where)
{
$this->where = !empty($where) ? $where : null;
}
// Sets the owner id of the content, usefull to add privacy
public function setOwner($user_id)
{
$this->owner = $user_id;
}
// Sets the user id of the request, usefull to add privacy
public function setRequester($user)
{
$this->requester = $user;
}
// Sets the category
public function setCategory($category)
{
$this->category = (int) $category;
}
// Sets the privacy layer of this listing
public function setPrivacy($privacy)
{
$this->privacy = $privacy;
}
// Sets the tools available for this listing (only if applies)
public function setTools($tools = [])
{
$this->tools = $tools;
}
public function bind($param, $value, $type = null)
{
$this->binds[] = array(
'param' => $param,
'value' => $value,
'type' => $type
);
}
public function getTotals($bool)
{
$this->get_totals = (bool) $bool;
}
/**
* Do the thing
* @Exeption 4xx
*/
public function exec($get_total_count = false)
{
$this->validateInput();
$tables = DB::getTables();
if (empty($this->requester)) {
$this->requester = Login::getUser();
} elseif (!is_array($this->requester)) {
$this->requester = User::getSingle($this->requester, 'id');
}
$joins = [
// Get image + storage + parent album + user uploader
'images' => [
'storages' => 'LEFT JOIN ' . $tables['storages'] . ' ON ' . $tables['images'] . '.image_storage_id = ' . $tables['storages'] . '.storage_id',
'users' => 'LEFT JOIN ' . $tables['users'] . ' ON ' . $tables['images'] . '.image_user_id = ' . $tables['users'] . '.user_id',
'albums' => 'LEFT JOIN ' . $tables['albums'] . ' ON ' . $tables['images'] . '.image_album_id = ' . $tables['albums'] . '.album_id',
'categories' => 'LEFT JOIN ' . $tables['categories'] . ' ON ' . $tables['images'] . '.image_category_id = ' . $tables['categories'] . '.category_id',
],
'users' => [],
'albums' => [
'users' => 'LEFT JOIN ' . $tables['users'] . ' ON ' . $tables['albums'] . '.album_user_id = ' . $tables['users'] . '.user_id'
]
];
if ($this->type == 'users' && $this->sort_type == 'views') {
$this->sort_type = 'content_views';
}
// Params hidden handler. Introduced to avoid stupid route.json.php cloning
// Same level content clauses (it won't help to filter joined tables)
if (!is_null($this->params_hidden)) {
// hide_empty
$emptyTypeClauses['users'][] = 'user_image_count > 0 OR user_avatar_filename IS NOT NULL OR user_background_filename IS NOT NULL';
if ($this->sort_type == 'views') {
$emptyTypeClauses['albums'][] = 'album_views > 0';
$emptyTypeClauses['images'][] = 'image_views > 0';
$emptyTypeClauses['users'][] = 'user_content_views > 0';
}
// Conditional album image count
if ($this->type == 'albums') {
if ($this->params_hidden['album_min_image_count'] > 0) {
$whereClauses[] = sprintf('album_image_count >= %d', $this->params_hidden['album_min_image_count']);
} else {
$emptyTypeClauses['albums'][] = 'album_image_count > 0';
}
}
if (array_key_exists($this->type, $emptyTypeClauses) && $this->params_hidden['hide_empty'] == 1) {
$whereClauses[] = '(' . implode(') AND (', $emptyTypeClauses[$this->type]) . ')';
}
// hide_banned
if ($this->params_hidden['hide_banned'] == 1) {
$whereClauses[] = '(' . $tables['users'] . '.user_status IS NULL OR ' . $tables['users'] . '.user_status <> "banned"' . ')';
}
// animated
if ($this->type == 'images' && $this->params_hidden['is_animated'] == 1) {
$whereClauses[] = 'image_is_animated = 1';
}
// We are getting clauser!.. got it? nvm...
if (!empty($whereClauses)) {
$whereClauses = join(' AND ', $whereClauses);
$this->where = (empty($this->where) ? 'WHERE ' : ($this->where . ' AND ')) . $whereClauses;
}
}
$type_singular = DB::getFieldPrefix($this->type);
// Attempt to add explicit clauses
if (!empty($this->where)) {
$where_clauses = explode(' ', str_ireplace('WHERE ', null, $this->where));
$where_arr = [];
foreach ($where_clauses as $clause) {
if (!preg_match('/\./', $clause)) {
$field_prefix = explode('_', $clause, 2)[0]; // field prefix (singular)
$table = DB::getTableFromFieldPrefix($field_prefix); // image -> chv_images
$table_prefix = G\get_app_setting('db_table_prefix');
$table_key = !empty($table_prefix) ? G\str_replace_first($table_prefix, null, $table) : $table;
if (array_key_exists($table_key, $tables)) {
$where_arr[] = $table . '.' . $clause;
} else {
$where_arr[] = $clause;
}
} else {
$where_arr[] = $clause; // Let it be
}
}
$this->where = 'WHERE ' . implode(' ', $where_arr);
}
// Add ID reservation clause
if ($this->type == 'images') {
$res_id_where = 'image_size > 0';
if (empty($this->where)) {
$this->where = 'WHERE ' . $res_id_where;
} else {
$this->where .= ' AND ' . $res_id_where;
}
}
// Add category clause
if ($this->type == 'images' && $this->category) {
$category_qry = $tables['images'] . '.image_category_id = ' . $this->category;
if (empty($this->where)) {
$this->where = 'WHERE ' . $category_qry;
} else {
$this->where .= ' AND ' . $category_qry;
}
}
// Privacy layer
if (!$this->requester['is_admin'] && in_array($this->type, ['images', 'albums', 'users']) && ((!$this->owner || !$this->requester) || $this->owner !== $this->requester['id'])) {
if (empty($this->where)) {
$this->where = 'WHERE ';
} else {
$this->where .= ' AND ';
}
$nsfw_off = $this->requester ? !$this->requester['show_nsfw_listings'] : !getSetting('show_nsfw_in_listings');
switch ($this->type) {
case 'images':
if ($nsfw_off) {
$nsfw_off_clause = $tables['images'] . '.image_nsfw = 0';
if ($this->requester) {
$this->where .= '(' . $nsfw_off_clause . ' OR (' . $tables['images'] . '.image_nsfw = 1 AND ' . $tables['images'] . '.image_user_id = ' . $this->requester['id'] . ')) AND ';
} else {
$this->where .= $nsfw_off_clause . ' AND ';
}
}
break;
case 'users':
$this->where .= $tables['users'] . '.user_is_private = 0';
break;
}
if ($this->type !== 'users') {
if (getSetting('website_privacy_mode') == 'public' || $this->privacy == 'private_but_link' || getSetting('website_content_privacy_mode') == 'default') {
$this->where .= '(' . $tables['albums'] . '.album_privacy NOT IN';
$privacy_modes = ['private', 'private_but_link', 'custom'];
if ($this->type != 'albums') {
$privacy_modes[] = 'password';
}
if (in_array($this->privacy, $privacy_modes)) {
unset($privacy_modes[array_search($this->privacy, $privacy_modes)]);
}
$this->where .= " (" . "'" . implode("','", $privacy_modes) . "'" . ") ";
$this->where .= "OR " . $tables['albums'] . '.album_privacy IS NULL';
if ($this->requester) {
$this->where .= ' OR ' . $tables['albums'] . '.album_user_id =' . $this->requester['id'];
}
$this->where .= ')';
} else {
$injected_requester = !$this->requester['id'] ? 0 : $this->requester['id'];
$this->where .= '(' . $tables['albums'] . '.album_user_id = ' . $injected_requester;
$this->where .= $this->type == 'albums' ? ')' : (' OR ' . $tables['images'] . '.image_user_id = ' . $injected_requester . ')');
}
}
}
$sort_field = $type_singular . '_' . $this->sort_type;
$key_field = $type_singular . '_id';
if ($this->seek) {
if (empty($this->where)) {
$this->where = 'WHERE ';
} else {
$this->where .= ' AND ';
}
if ($this->reverse) {
$this->sort_order = $this->sort_order == 'asc' ? 'desc' : 'asc';
}
$signo = $this->sort_order == 'desc' ? '<=' : '>=';
if ($this->sort_type == 'id') {
$this->where .= $sort_field . ' ' . $signo . ' :seek';
$this->binds[] = [
'param' => 'seek',
'value' => $this->seek
];
} else {
$signo = $this->sort_order == 'desc' ? '<' : '>';
$this->where .= '((' . $sort_field . ' ' . $signo . ' :seekSort) OR (' . $sort_field . ' = :seekSort AND ' . $key_field . ' ' . $signo . '= :seekKey))';
$this->binds[] = [
'param' => 'seekSort',
'value' => $this->seek[0],
];
$this->binds[] = [
'param' => 'seekKey',
'value' => $this->seek[1],
];
}
}
if (!empty($this->where)) {
$this->where = "\n" . $this->where;
}
$sort_order = strtoupper($this->sort_order);
$table_order = DB::getTableFromFieldPrefix($type_singular);
$order_by = "\n" . 'ORDER BY ' . $table_order . '.' . $sort_field . ' ' . $sort_order;
if ($this->sort_type != 'id') {
$order_by .= ', ' . $table_order . '.' . $key_field . ' ' . $sort_order;
}
// $limit = "\n" . 'LIMIT ' . $this->offset . ',' . ($this->limit + 1);
$limit = "\n" . 'LIMIT ' . ($this->limit + 1); // +1 allows to fetch "one extra" to detect prev/next pages
$base_table = $tables[$this->type];
// Normal query
if (empty($joins[$this->type])) {
$query = 'SELECT * FROM ' . $base_table;
$query .= $this->where . $order_by . $limit;
// Alternative query
} else {
if (!empty($this->where)) {
preg_match_all('/' . G\get_app_setting('db_table_prefix') . '([\w_]+)\./', $this->where, $where_tables);
$where_tables = array_values(array_diff(array_unique($where_tables[1]), [$this->type]));
} else {
$where_tables = false;
}
if ($where_tables) {
$join_tables = $where_tables;
} else {
reset($joins);
$join_tables = [key($joins)];
}
$join = null;
foreach ($join_tables as $join_table) {
if (!empty($joins[$this->type][$join_table])) {
$join .= "\n" . $joins[$this->type][$join_table];
unset($joins[$this->type][$join_table]);
}
}
// Get rid of the original Exif data (for listings)
$null_db = $this->type == 'images' ? ', NULL as image_original_exifdata ' : null;
$query = 'SELECT * ' . $null_db . 'FROM (SELECT * FROM ' . $base_table . $join . $this->where . $order_by . $limit . ') ' . $base_table;
if (!empty($joins[$this->type])) {
$query .= "\n" . implode("\n", $joins[$this->type]);
}
$query .= $order_by;
}
try {
$db = DB::getInstance();
// G\debug($query);
$db->query($query);
if (is_array($this->binds)) {
foreach ($this->binds as $bind) {
$db->bind($bind['param'], $bind['value'], $bind['type']);
}
}
$this->output = $db->fetchAll();
$this->output_count = $db->rowCount();
$this->has_page_next = $db->rowCount() > $this->limit;
if ($this->reverse) {
$this->output = array_reverse($this->output);
}
$start = current($this->output);
$end = end($this->output);
$seekEnd = $end[$sort_field];
$seekStart = $start[$sort_field];
if ($this->sort_type == 'id') {
$seekEnd = encodeID($seekEnd);
$seekStart = encodeID($seekStart);
} else {
$seekEnd .= '.' . encodeID($end[$key_field]);
$seekStart .= '.' . encodeID($start[$key_field]);
}
$this->seekEnd = $seekEnd;
$this->seekStart = $seekStart;
if ($db->rowCount() > $this->limit) {
$array_fn = 'array_' . ($this->reverse ? 'shift' : 'pop');
array_pop($this->output);
}
$this->output = G\safe_html($this->output);
$this->count = count($this->output);
$this->nsfw = false;
$this->output_assoc = [];
$formatfn = 'CHV\\' . ucfirst(substr($this->type, 0, -1));
foreach ($this->output as $k => $v) {
$val = $formatfn::formatArray($v);
$this->output_assoc[] = $val;
if (!$this->nsfw and $val['nsfw']) {
$this->nsfw = true;
}
}
$this->sfw = !$this->nsfw;
$this->has_page_prev = $this->offset > 0;
G\handler::setCond('show_viewer_zero', isset($_REQUEST['viewer']));
} catch (Exception $e) {
throw new ListingException($e->getMessage(), 400);
}
// Get album slices and stuff
if ($this->type == 'albums' and $this->output) {
$album_slice_qry_tpl = 'SELECT * FROM ' . $tables['images'] . ' LEFT JOIN ' . $tables['storages'] . ' ON ' . $tables['images'] . '.image_storage_id = ' . $tables['storages'] . '.storage_id WHERE ' . $tables['images'] . '.image_album_id=%ALBUM_ID% ORDER BY ' . $tables['images'] . '.image_id ASC LIMIT 0,5';
$albums_slice_qry_arr = [];
$albums_mapping = [];
foreach ($this->output as $k => &$album) {
// Album count
if ($album['album_image_count'] < 0) {
$album['album_image_count'] = 0;
}
$album['album_image_count_label'] = _n('image', 'images', $album['album_image_count']);
// Album slice
$albums_slice_qry_arr[] = str_replace('%ALBUM_ID%', $album['album_id'], $album_slice_qry_tpl);
// Album mapping
$albums_mapping[$album['album_id']] = $k;
}
$albums_slice_qry = '(' . implode(') ' . "\n" . 'UNION ALL ' . "\n" . '(', $albums_slice_qry_arr) . ')';
try {
$db->query($albums_slice_qry);
$albums_slice = $db->fetchAll();
if ($albums_slice) {
foreach ($albums_slice as $slice) {
$album_key = $albums_mapping[$slice['image_album_id']];
if (!array_key_exists('album_images_slice', $this->output[$album_key])) {
$this->output[$album_key]['album_images_slice'] = [];
}
$this->output[$album_key]['album_images_slice'][] = $slice;
}
}
} catch (Exception $e) {
throw new ListingException($e->getMessage(), 400);
}
}
}
public static function getTabs($args = [], $expanded = false)
{
$default = [
'list' => true,
'REQUEST' => $_REQUEST,
'listing' => 'explore',
'basename' => G\get_route_name(),
'tools' => true,
'tools_available' => [],
];
$args = array_merge($default, $args);
// Fix lazy basenames
if (strpos($args['basename'], G\get_base_url()) !== false) {
$args['basename'] = G\get_base_url() == $args['basename'] ? null : G\str_replace_first(G\get_base_url() . '/', null, $args['basename']);
}
// Semantics -> trending | popular | recent
$semantics = [
'recent' => [
'label' => _s('Recent'),
'content' => 'all',
'sort' => 'date_desc',
],
'trending' => [
'label' => _s('Trending'),
'content' => 'all',
'sort' => 'views_desc',
],
];
// Criteria -> images | albums | users
// Criteria -> [CONTENT TABS]
$criterias = [
'top-users' => [
'label' => _s('Top users'),
'sort' => 'image_count_desc',
'content' => 'users',
],
'most-recent' => [
'label' => _s('Most recent'),
'sort' => 'date_desc',
'content' => 'all',
],
'most-oldest' => [
'label' => _s('Oldest'),
'sort' => 'date_asc',
'content' => 'all',
],
'most-viewed' => [
'label' => _s('Most viewed'),
'sort' => 'views_desc',
'content' => 'all',
],
];
$criterias['album-az-asc'] = [
'label' => 'AZ',
'sort' => 'name_asc',
'content' => 'albums',
];
$criterias['image-az-asc'] = [
'label' => 'AZ',
'sort' => 'title_asc',
'content' => 'images',
];
$criterias['user-az-asc'] = [
'label' => 'AZ',
'sort' => 'username_asc',
'content' => 'users',
];
$listings = [
'explore' => [
'label' => _s('Explore'),
'content' => 'images',
],
'animated' => [
'label' => _s('Animated'),
'content' => 'images',
'where' => 'image_is_animated = 1',
'semantic' => true,
],
'search' => [
'label' => _s('Search'),
'content' => 'all',
],
'users' => [
'label' => _s('People'),
'content' => 'users',
],
'images' => [
'label' => _n('Image', 'Images', 2),
'content' => 'images',
],
'albums' => [
'label' => _n('Album', 'Albums', 2),
'content' => 'albums',
],
];
$listings = array_merge($listings, $semantics);
$parameters = $listings[$args['listing']];
if (is_array($args['exclude_criterias'])) {
foreach ($args['exclude_criterias'] as $exclude) {
if (array_key_exists($exclude, $criterias)) {
unset($criterias[$exclude]);
}
}
}
// Content -> most recent | oldest | most viewed | most liked
// Content -> [CRITERIA TABS]
$contents = [
'images' => [
'label' => _n('Image', 'Images', 2),
],
'albums' => [
'label' => _n('Album', 'Albums', 2),
],
'users' => [
'label' => _n('User', 'Users', 2),
]
];
$i = 0;
$currentKey = null;
foreach (($parameters['content'] == 'all' ? $contents : ($parameters['semantic'] ? $semantics : $criterias)) as $k => $v) {
if ($parameters['content'] == 'all') {
$content = $k;
$id = 'list-' . $args['listing'] . '-' . $content; // list-popular-images
$sort = $parameters['sort'];
} else {
$content = $parameters['content'];
if ($v['content'] !== 'all' && $v['content'] !== $content) {
continue;
}
$id = 'list-' . $k; // list-most-oldest
$sort = $v['sort'];
}
if (!$content) {
$content = 'images'; // explore
}
$basename = $args['basename'];
$default_params = [
'list' => $content,
'sort' => $sort,
'page' => '1',
];
$params = $args['params'] ?: $default_params;
foreach ((array) $args['params_remove_keys'] as $key) {
unset($params[$key]);
}
if (is_array($args['params']) && array_key_exists('q', $args['params']) && $args['listing'] == 'search') {
$args['params_hidden']['list'] = $content;
$basename .= '/' . $content;
}
foreach ((array) $args['params_hidden'] as $kk => $vv) {
if (array_key_exists($kk, $params)) {
unset($params[$kk]);
}
}
$http_build_query = http_build_query($params);
$url = (filter_var($basename, FILTER_VALIDATE_URL) ? rtrim($basename, '/') : G\get_base_url($basename)) . '/?' . $http_build_query;
$current = isset($args['REQUEST']['sort']) ? $args['REQUEST']['sort'] == $v['sort'] : false;
if ($i == 0 && !$current) {
$current = !$args['REQUEST']['sort'];
}
if ($current && is_null($currentKey)) {
$currentKey = $i;
}
$tab = [
'list' => (bool) $args['list'],
'tools' => $content == 'users' ? false : (bool) $args['tools'],
'tools_available' => $args['tools_available'],
'label' => $v['label'],
'id' => $id,
'params' => $http_build_query, // Es que como explicar la magia que tiene su manera de enamorar...
'current' => (bool) $current,
'type' => $content,
'url' => $url
];
if ($args['tools_available'] && !G\Handler::getCond('allowed_to_delete_content') && array_key_exists('delete', $args['tools_available'])) {
unset($args['tools_available']['delete']);
}
if ($args['tools_available'] == null) {
unset($tab['tools_available']);
}
if ($args['params_hidden']) {
$tab['params_hidden'] = http_build_query($args['params_hidden']);
}
$tabs[] = $tab;
unset($id, $params, $basename, $http_build_query, $content, $current);
$i++;
}
if (is_null($currentKey)) {
if ($parameters['content'] == 'all') {
foreach ($tabs as $k => &$v) {
if (isset($args['REQUEST']['list']) && $v['type'] == $args['REQUEST']['list']) {
$v['current'] = true;
$currentKey = $k;
break;
}
}
} else {
$currentKey = 0;
$tabs[0]['current'] = true;
}
}
if ($expanded) {
return ['tabs' => $tabs, 'currentKey' => $currentKey];
}
return $tabs;
}
/**
* validate_input aka "first stage validation"
* This checks for valid input source data before exec
* @Exception 1XX
*/
protected function validateInput()
{
self::setValidSortTypes();
if (empty($this->offset)) {
$this->offset = 0;
}
// Missing values
$check_missing = ['type', 'offset', 'limit', 'sort_type', 'sort_order'];
missing_values_to_exception($this, 'CHV\ListingException', $check_missing, 100);
// Validate type
if (!in_array($this->type, self::$valid_types)) {
throw new ListingException('Invalid $type "' . $this->type . '"', 110);
}
// Validate limits
if ($this->offset == 0 && $this->limit == 0) {
throw new ListingException('$offset and $limit are equal to 0 (zero)', 120);
}
if ($this->offset < 0 || $this->limit < 0) {
throw new ListingException('Limit integrity violation', 121);
}
// Validate sort type
if (!in_array($this->sort_type, self::$valid_sort_types)) {
throw new ListingException('Invalid $sort_type "' . $this->sort_type . '"', 130);
}
// Validate sort order
if (!preg_match('/^(asc|desc)$/', $this->sort_order)) {
throw new ListingException('Invalid $sort_order "' . $this->sort_order . '"', 140);
}
}
// Handler for all those switcheable sort options (based on on/off settings)
protected static function setValidSortTypes()
{
}
public function htmlOutput($tpl_list = null)
{
if (!is_array($this->output)) {
return;
}
if (is_null($tpl_list)) {
$tpl_list = $this->type ?: 'images';
}
$directory = new \RecursiveDirectoryIterator(G_APP_PATH_THEME . 'tpl_list_item/');
$iterator = new \RecursiveIteratorIterator($directory);
$regex = new \RegexIterator($iterator, '/^.+\.php$/i', \RecursiveRegexIterator::GET_MATCH);
$list_item_template = [];
foreach ($regex as $file) {
$file = G\forward_slash($file[0]);
$key = preg_replace('/\\.[^.\\s]{3,4}$/', '', str_replace(G_APP_PATH_THEME, null, $file));
$override_file = G\str_replace_first(G_APP_PATH_THEME, G_APP_PATH_THEME . 'overrides/', $file);
if (is_readable($override_file)) {
$file = $override_file;
}
ob_start();
require($file);
$file_get_contents = ob_get_contents();
ob_end_clean();
$list_item_template[$key] = $file_get_contents;
}
$html_output = '';
$tpl_list = preg_replace('/s$/', '', $tpl_list);
if (function_exists('get_peafowl_item_list')) {
$render = 'get_peafowl_item_list';
} else {
$render = 'CHV\Render\get_peafowl_item_list';
}
$tools = $this->tools ?: null;
$requester = Login::getUser();
foreach ($this->output as $row) {
switch ($tpl_list) {
case 'image':
case 'user/image':
case 'album/image':
default: // key thing here...
$Class = 'CHV\Image';
break;
case 'album':
case 'user/album':
$Class = 'CHV\Album';
break;
case 'user':
case 'user/user':
$Class = 'CHV\User';
break;
}
$item = $Class::formatArray($row);
$html_output .= $render($tpl_list, $item, $list_item_template, $requester, $tools);
}
return $html_output;
}
public static function getAlbumHtml($album_id, $template = 'user/albums')
{
try {
$album = new Listing;
$album->setType('albums');
$album->setOffset(0);
$album->setLimit(1);
$album->setSortType('date');
$album->setSortOrder('desc');
$album->setWhere('WHERE album_id=:album_id');
$album->bind(':album_id', $album_id);
$album->exec();
return $album->htmlOutput($template);
} catch (Exception $e) {
throw new ListingException($e->getMessage(), 400);
}
}
public static function getParams($json_call = false)
{
self::setValidSortTypes();
$items_per_page = getSetting('listing_items_per_page');
$listing_pagination_mode = getSetting('listing_pagination_mode');
$params = [];
$params['offset'] = 0;
$params['items_per_page'] = $items_per_page;
if (!$json_call and $listing_pagination_mode == 'endless') {
$params['page'] = max(intval($_REQUEST['page']), 1);
$params['limit'] = $params['items_per_page'] * $params['page'];
// $params['offset'] = 0;
// Switch endless to classic if we are dealing with large listings (from GET)
if ($params['limit'] > getSetting('listing_safe_count')) {
$listing_pagination_mode = 'classic';
Settings::setValue('listing_pagination_mode', $listing_pagination_mode);
}
}
if (isset($_REQUEST['pagination']) or $listing_pagination_mode == 'classic') { // Static single page display
$params['page'] = $_REQUEST['page'] ? intval($_REQUEST['page']) - 1 : 0;
$params['limit'] = $params['items_per_page'];
$params['offset'] = $params['page'] * $params['limit']; // TODO: Get rid
}
if ($json_call) {
$params = array_merge($params, [
'page' => $_REQUEST['page'] ? $_REQUEST['page'] - 1 : 0,
'limit' => $items_per_page
]);
$params['offset'] = $params['page'] * $params['limit'] + ($_REQUEST['offset'] ? $_REQUEST['offset'] : 0); // TODO: Get rid
}
$default_sort = [
0 => 'date',
1 => 'desc'
];
preg_match('/(.*)_(asc|desc)/', $_REQUEST['sort'], $sort_matches);
$params['sort'] = array_slice($sort_matches, 1);
// Empty sort
if (count($params['sort']) !== 2) {
$params['sort'] = $default_sort;
}
// Check sort type
if (!in_array($params['sort'][0], self::$valid_sort_types)) {
$params['sort'][0] = $default_sort[0];
}
// Check sort order
if (!in_array($params['sort'][1], ['asc', 'desc'])) {
$params['sort'][1] = $default_sort[1];
}
// $_REQUEST is already urldecoded
if ($_REQUEST['seek']) {
$params['seek'] = $_REQUEST['seek'];
} elseif ($_REQUEST['peek']) {
$params['seek'] = $_REQUEST['peek'];
$params['reverse'] = true;
}
$params['page_show'] = isset($_REQUEST['page']) ? intval($_REQUEST['page']) : null;
return $params;
}
}
class ListingException extends Exception
{
}