mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-05-14 18:23:52 -04:00
Sort by category title, feed title (#7702)
* Sort by category name, feed name fix https://github.com/FreshRSS/FreshRSS/issues/7698 Note that sorting is done with the default SQL collation for now, meaning that lower-case vs. upper-case and diacritics are influencing the sorting order. Improvements left for future work. Watch out that those sorting criteria are slower due to additional joins, additional requests, and poorer indexes. * i18n:pl Co-authored-by: Inverle <inverle@proton.me> * i18n: nl Co-authored-by: Frans de Jonge <fransdejonge@gmail.com> * Fix preserve sort --------- Co-authored-by: Inverle <inverle@proton.me> Co-authored-by: Frans de Jonge <fransdejonge@gmail.com>
This commit is contained in:
committed by
GitHub
parent
8a44d1045a
commit
5f61e426dc
@@ -283,15 +283,30 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
||||
$id_min = (time() - (FreshRSS_Context::$sinceHours * 3600)) . '000000';
|
||||
}
|
||||
|
||||
$continuation_value = 0;
|
||||
$continuation_values = [];
|
||||
if (FreshRSS_Context::$continuation_id !== '0') {
|
||||
if (in_array(FreshRSS_Context::$sort, ['date', 'link', 'title'], true)) {
|
||||
if (in_array(FreshRSS_Context::$sort, ['c.name', 'date', 'f.name', 'link', 'title'], true)) {
|
||||
$pagingEntry = $entryDAO->searchById(FreshRSS_Context::$continuation_id);
|
||||
$continuation_value = $pagingEntry === null ? 0 : match (FreshRSS_Context::$sort) {
|
||||
|
||||
if ($pagingEntry !== null && in_array(FreshRSS_Context::$sort, ['c.name', 'f.name'], true)) {
|
||||
// We most likely already have the feed object in cache
|
||||
$feed = FreshRSS_Category::findFeed(FreshRSS_Context::categories(), $pagingEntry->feedId());
|
||||
if ($feed !== null) {
|
||||
$pagingEntry->_feed($feed);
|
||||
}
|
||||
}
|
||||
|
||||
$continuation_values[] = $pagingEntry === null ? 0 : match (FreshRSS_Context::$sort) {
|
||||
'c.name' => $pagingEntry->feed()?->category()?->name() ?? '',
|
||||
'date' => $pagingEntry->date(true),
|
||||
'f.name' => $pagingEntry->feed()?->name() ?? '',
|
||||
'link' => $pagingEntry->link(true),
|
||||
'title' => $pagingEntry->title(),
|
||||
};
|
||||
if ($pagingEntry !== null && FreshRSS_Context::$sort === 'c.name') {
|
||||
// Secondary sort criterion
|
||||
$continuation_values[] = $pagingEntry->feed()?->name() ?? '';
|
||||
}
|
||||
} elseif (FreshRSS_Context::$sort === 'rand') {
|
||||
FreshRSS_Context::$continuation_id = '0';
|
||||
}
|
||||
@@ -300,7 +315,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
||||
foreach ($entryDAO->listWhere(
|
||||
$type, $id, FreshRSS_Context::$state, FreshRSS_Context::$search,
|
||||
id_min: $id_min, id_max: FreshRSS_Context::$id_max, sort: FreshRSS_Context::$sort, order: FreshRSS_Context::$order,
|
||||
continuation_id: FreshRSS_Context::$continuation_id, continuation_value: $continuation_value,
|
||||
continuation_id: FreshRSS_Context::$continuation_id, continuation_values: $continuation_values,
|
||||
limit: $postsPerPage ?? FreshRSS_Context::$number, offset: FreshRSS_Context::$offset) as $entry) {
|
||||
yield $entry;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ final class FreshRSS_Context {
|
||||
public static int $state = 0;
|
||||
/** @var 'ASC'|'DESC' */
|
||||
public static string $order = 'DESC';
|
||||
/** @var 'id'|'date'|'link'|'title'|'rand' */
|
||||
/** @var 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand' */
|
||||
public static string $sort = 'id';
|
||||
public static int $number = 0;
|
||||
public static int $offset = 0;
|
||||
@@ -237,7 +237,7 @@ final class FreshRSS_Context {
|
||||
$order = Minz_Request::paramString('order', true) ?: FreshRSS_Context::userConf()->sort_order;
|
||||
self::$order = in_array($order, ['ASC', 'DESC'], true) ? $order : 'DESC';
|
||||
$sort = Minz_Request::paramString('sort', true) ?: FreshRSS_Context::userConf()->sort;
|
||||
self::$sort = in_array($sort, ['id', 'date', 'link', 'title', 'rand'], true) ? $sort : 'id';
|
||||
self::$sort = in_array($sort, ['id', 'c.name', 'date', 'f.name', 'link', 'title', 'rand'], true) ? $sort : 'id';
|
||||
self::$number = Minz_Request::paramInt('nb') ?: FreshRSS_Context::userConf()->posts_per_page;
|
||||
if (self::$number > FreshRSS_Context::userConf()->max_posts_per_rss) {
|
||||
self::$number = max(
|
||||
|
||||
@@ -1184,13 +1184,15 @@ SQL;
|
||||
/**
|
||||
* @param numeric-string $id_min
|
||||
* @param numeric-string $id_max
|
||||
* @param 'id'|'date'|'link'|'title'|'rand' $sort
|
||||
* @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand' $sort
|
||||
* @param 'ASC'|'DESC' $order
|
||||
* @param numeric-string $continuation_id
|
||||
* @param list<string|int> $continuation_values
|
||||
* @return array{0:list<int|string>,1:string}
|
||||
*/
|
||||
protected function sqlListEntriesWhere(string $alias = '', int $state = FreshRSS_Entry::STATE_ALL, ?FreshRSS_BooleanSearch $filters = null,
|
||||
string $id_min = '0', string $id_max = '0', string $sort = 'id', string $order = 'DESC',
|
||||
string $continuation_id = '0', string|int $continuation_value = 0): array {
|
||||
string $continuation_id = '0', array $continuation_values = []): array {
|
||||
$search = ' ';
|
||||
$values = [];
|
||||
if ($state & FreshRSS_Entry::STATE_ANDS) {
|
||||
@@ -1250,13 +1252,29 @@ SQL;
|
||||
$values[] = $id_min;
|
||||
}
|
||||
|
||||
if ($continuation_id !== '0' && in_array($sort, ['date', 'link', 'title'], true)) {
|
||||
if ($continuation_id !== '0' && in_array($sort, ['c.name', 'date', 'f.name', 'link', 'title'], true)) {
|
||||
$sign = $order === 'ASC' ? '>' : '<';
|
||||
$orderBy = match ($sort) {
|
||||
'c.name' => 'c.name',
|
||||
'f.name' => 'f.name',
|
||||
default => $alias . $sort,
|
||||
};
|
||||
// Keyset pagination (Compatibility syntax due to poor performance of tuple syntax in MySQL https://bugs.mysql.com/bug.php?id=104128)
|
||||
$search .= "AND ({$alias}{$sort} {$sign} ? OR ({$alias}{$sort} = ? AND {$alias}id {$sign}= ?)) ";
|
||||
$values[] = $continuation_value;
|
||||
$values[] = $continuation_value;
|
||||
$values[] = $continuation_id;
|
||||
if ($sort === 'c.name') {
|
||||
// Includes a secondary sort by feed name
|
||||
$search .= "AND ((c.name {$sign} ?) OR (c.name = ? AND f.name {$sign} ?) OR (c.name = ? AND f.name = ? AND {$alias}id {$sign}= ?)) ";
|
||||
$values[] = $continuation_values[0];
|
||||
$values[] = $continuation_values[0];
|
||||
$values[] = $continuation_values[1];
|
||||
$values[] = $continuation_values[0];
|
||||
$values[] = $continuation_values[1];
|
||||
$values[] = $continuation_id;
|
||||
} else {
|
||||
$search .= "AND ({$orderBy} {$sign} ? OR ({$orderBy} = ? AND {$alias}id {$sign}= ?)) ";
|
||||
$values[] = $continuation_values[0];
|
||||
$values[] = $continuation_values[0];
|
||||
$values[] = $continuation_id;
|
||||
}
|
||||
}
|
||||
|
||||
if ($filters !== null && count($filters->searches()) > 0) {
|
||||
@@ -1276,15 +1294,16 @@ SQL;
|
||||
* @param int $id category/feed/tag ID
|
||||
* @param numeric-string $id_min
|
||||
* @param numeric-string $id_max
|
||||
* @param 'id'|'date'|'link'|'title'|'rand' $sort
|
||||
* @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand' $sort
|
||||
* @param 'ASC'|'DESC' $order
|
||||
* @param numeric-string $continuation_id
|
||||
* @param list<string|int> $continuation_values
|
||||
* @return array{0:list<int|string>,1:string}
|
||||
* @throws FreshRSS_EntriesGetter_Exception
|
||||
*/
|
||||
private function sqlListWhere(string $type = 'a', int $id = 0, int $state = FreshRSS_Entry::STATE_ALL, ?FreshRSS_BooleanSearch $filters = null,
|
||||
string $id_min = '0', string $id_max = '0', string $sort = 'id', string $order = 'DESC',
|
||||
string $continuation_id = '0', string|int $continuation_value = 0, int $limit = 1, int $offset = 0): array {
|
||||
string $continuation_id = '0', array $continuation_values = [], int $limit = 1, int $offset = 0): array {
|
||||
if (!$state) {
|
||||
$state = FreshRSS_Entry::STATE_ALL;
|
||||
}
|
||||
@@ -1334,21 +1353,28 @@ SQL;
|
||||
}
|
||||
|
||||
$order = in_array($order, ['ASC', 'DESC'], true) ? $order : 'DESC';
|
||||
$sort = in_array($sort, ['id', 'date', 'link', 'title', 'rand'], true) ? $sort : 'id';
|
||||
$orderBy = ($sort === 'rand' ? static::sqlRandom() : 'e.' . $sort);
|
||||
$sort = in_array($sort, ['id', 'c.name', 'date', 'f.name', 'link', 'title', 'rand'], true) ? $sort : 'id';
|
||||
$orderBy = match ($sort) {
|
||||
'c.name' => 'c.name',
|
||||
'f.name' => 'f.name',
|
||||
'rand' => static::sqlRandom(),
|
||||
default => 'e.' . $sort,
|
||||
};
|
||||
[$searchValues, $search] = $this->sqlListEntriesWhere(alias: 'e.', state: $state, filters: $filters, id_min: $id_min, id_max: $id_max,
|
||||
sort: $sort, order: $order, continuation_id: $continuation_id, continuation_value: $continuation_value);
|
||||
sort: $sort, order: $order, continuation_id: $continuation_id, continuation_values: $continuation_values);
|
||||
|
||||
return [array_merge($values, $searchValues), 'SELECT '
|
||||
. ($type === 'T' ? 'DISTINCT ' : '')
|
||||
. 'e.id'
|
||||
. ($type === 'T' && $orderBy !== 'e.id' ? ', ' . $orderBy : '') // SELECT DISTINCT, ORDER BY expressions must appear in SELECT
|
||||
. ($type === 'T' && $sort !== 'id' ? ', ' . $orderBy : '') // SELECT DISTINCT, ORDER BY expressions must appear in SELECT
|
||||
. ' FROM `_entry` e '
|
||||
. 'INNER JOIN `_feed` f ON e.id_feed = f.id '
|
||||
. 'INNER JOIN `_feed` f ON f.id = e.id_feed '
|
||||
. ($sort === 'c.name' ? 'INNER JOIN `_category` c ON c.id = f.category ' : '')
|
||||
. ($type === 't' || $type === 'T' ? 'INNER JOIN `_entrytag` et ON et.id_entry = e.id ' : '')
|
||||
. 'WHERE ' . $where
|
||||
. $search
|
||||
. 'ORDER BY ' . $orderBy . ' ' . $order
|
||||
. ($sort === 'c.name' ? ', f.name ' . $order : '') // Secondary sort
|
||||
. ($sort === 'id' ? '' : ', e.id ' . $order) // For keyset pagination
|
||||
. ($limit > 0 ? ' LIMIT ' . $limit : '') // http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/
|
||||
. ($offset > 0 ? ' OFFSET ' . $offset : '')
|
||||
@@ -1360,29 +1386,44 @@ SQL;
|
||||
* @param int $id category/feed/tag ID
|
||||
* @param numeric-string $id_min
|
||||
* @param numeric-string $id_max
|
||||
* @param 'id'|'date'|'link'|'title'|'rand' $sort
|
||||
* @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand' $sort
|
||||
* @param 'ASC'|'DESC' $order
|
||||
* @param numeric-string $continuation_id
|
||||
* @param list<string|int> $continuation_values
|
||||
* @throws FreshRSS_EntriesGetter_Exception
|
||||
*/
|
||||
private function listWhereRaw(string $type = 'a', int $id = 0, int $state = FreshRSS_Entry::STATE_ALL, ?FreshRSS_BooleanSearch $filters = null,
|
||||
string $id_min = '0', string $id_max = '0', string $sort = 'id', string $order = 'DESC',
|
||||
string $continuation_id = '0', string|int $continuation_value = 0, int $limit = 1, int $offset = 0): PDOStatement|false {
|
||||
string $continuation_id = '0', array $continuation_values = [], int $limit = 1, int $offset = 0): PDOStatement|false {
|
||||
$order = in_array($order, ['ASC', 'DESC'], true) ? $order : 'DESC';
|
||||
$sort = in_array($sort, ['id', 'date', 'link', 'title', 'rand'], true) ? $sort : 'id';
|
||||
$sort = in_array($sort, ['id', 'c.name', 'date', 'f.name', 'link', 'title', 'rand'], true) ? $sort : 'id';
|
||||
|
||||
[$values, $sql] = $this->sqlListWhere($type, $id, $state, $filters, id_min: $id_min, id_max: $id_max, sort: $sort, order: $order,
|
||||
continuation_id: $continuation_id, continuation_value: $continuation_value, limit: $limit, offset: $offset);
|
||||
continuation_id: $continuation_id, continuation_values: $continuation_values, limit: $limit, offset: $offset);
|
||||
|
||||
$orderBy = ($sort === 'rand' ? static::sqlRandom() : 'e0.' . $sort);
|
||||
$orderBy = match ($sort) {
|
||||
'c.name' => 'c0.name',
|
||||
'f.name' => 'f0.name',
|
||||
'rand' => static::sqlRandom(),
|
||||
default => 'e0.' . $sort,
|
||||
};
|
||||
$content = static::isCompressed() ? 'UNCOMPRESS(e0.content_bin) AS content' : 'e0.content';
|
||||
$hash = static::sqlHexEncode('e0.hash');
|
||||
$sql = <<<SQL
|
||||
SELECT e0.id, e0.guid, e0.title, e0.author, {$content}, e0.link, e0.date, {$hash} AS hash, e0.is_read, e0.is_favorite, e0.id_feed, e0.tags, e0.attributes
|
||||
FROM `_entry` e0
|
||||
INNER JOIN ({$sql}) e2 ON e2.id=e0.id
|
||||
ORDER BY {$orderBy} {$order}
|
||||
SQL;
|
||||
if ($sort === 'f.name' || $sort === 'c.name') {
|
||||
$sql .= ' INNER JOIN `_feed` f0 ON f0.id = e0.id_feed ';
|
||||
}
|
||||
if ($sort === 'c.name') {
|
||||
$sql .= ' INNER JOIN `_category` c0 ON c0.id = f0.category ';
|
||||
}
|
||||
$sql .= ' ORDER BY ' . $orderBy . ' ' . $order;
|
||||
if ($sort === 'c.name') {
|
||||
$sql .= ', f0.name ' . $order; // Secondary sort
|
||||
}
|
||||
if ($sort !== 'id') {
|
||||
// For keyset pagination
|
||||
$sql .= ', e0.id ' . $order;
|
||||
@@ -1395,7 +1436,7 @@ SQL;
|
||||
/** @var array{0:string,1:int,2:string} $info */
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->listWhereRaw($type, $id, $state, $filters, id_min: $id_min, id_max: $id_max, sort: $sort, order: $order,
|
||||
continuation_id: $continuation_id, continuation_value: $continuation_value, limit: $limit, offset: $offset);
|
||||
continuation_id: $continuation_id, continuation_values: $continuation_values, limit: $limit, offset: $offset);
|
||||
}
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
@@ -1407,17 +1448,18 @@ SQL;
|
||||
* @param int $id category/feed/tag ID
|
||||
* @param numeric-string $id_min
|
||||
* @param numeric-string $id_max
|
||||
* @param 'id'|'date'|'link'|'title'|'rand' $sort
|
||||
* @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand' $sort
|
||||
* @param 'ASC'|'DESC' $order
|
||||
* @param numeric-string $continuation_id
|
||||
* @param list<string|int> $continuation_values
|
||||
* @return Traversable<FreshRSS_Entry>
|
||||
* @throws FreshRSS_EntriesGetter_Exception
|
||||
*/
|
||||
public function listWhere(string $type = 'a', int $id = 0, int $state = FreshRSS_Entry::STATE_ALL, ?FreshRSS_BooleanSearch $filters = null,
|
||||
string $id_min = '0', string $id_max = '0', string $sort = 'id', string $order = 'DESC',
|
||||
string $continuation_id = '0', string|int $continuation_value = 0, int $limit = 1, int $offset = 0): Traversable {
|
||||
string $continuation_id = '0', array $continuation_values = [], int $limit = 1, int $offset = 0): Traversable {
|
||||
$stm = $this->listWhereRaw($type, $id, $state, $filters, id_min: $id_min, id_max: $id_max, sort: $sort, order: $order,
|
||||
continuation_id: $continuation_id, continuation_value: $continuation_value, limit: $limit, offset: $offset);
|
||||
continuation_id: $continuation_id, continuation_values: $continuation_values, limit: $limit, offset: $offset);
|
||||
if ($stm !== false) {
|
||||
while (is_array($row = $stm->fetch(PDO::FETCH_ASSOC))) {
|
||||
/** @var array{'id':string,'id_feed':int,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int,
|
||||
@@ -1473,16 +1515,17 @@ SQL;
|
||||
* @param int $id category/feed/tag ID
|
||||
* @param numeric-string $id_min
|
||||
* @param numeric-string $id_max
|
||||
* @param numeric-string $continuation_id
|
||||
* @param 'ASC'|'DESC' $order
|
||||
* @param numeric-string $continuation_id
|
||||
* @param list<string|int> $continuation_values
|
||||
* @return list<numeric-string>|null
|
||||
* @throws FreshRSS_EntriesGetter_Exception
|
||||
*/
|
||||
public function listIdsWhere(string $type = 'a', int $id = 0, int $state = FreshRSS_Entry::STATE_ALL, ?FreshRSS_BooleanSearch $filters = null,
|
||||
string $id_min = '0', string $id_max = '0', string $order = 'DESC',
|
||||
string $continuation_id = '0', string|int $continuation_value = 0, int $limit = 1, int $offset = 0): ?array {
|
||||
string $continuation_id = '0', array $continuation_values = [], int $limit = 1, int $offset = 0): ?array {
|
||||
[$values, $sql] = $this->sqlListWhere($type, $id, $state, $filters, id_min: $id_min, id_max: $id_max, order: $order,
|
||||
continuation_id: $continuation_id, continuation_value: $continuation_value, limit: $limit, offset: $offset);
|
||||
continuation_id: $continuation_id, continuation_values: $continuation_values, limit: $limit, offset: $offset);
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
/** @var list<int|numeric-string> $res */
|
||||
|
||||
@@ -52,7 +52,7 @@ declare(strict_types=1);
|
||||
* @property bool $show_nav_buttons
|
||||
* @property 'big'|'small'|'none' $mark_read_button
|
||||
* @property 'ASC'|'DESC' $sort_order
|
||||
* @property 'id'|'date'|'link'|'title'|'rand' $sort
|
||||
* @property 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand' $sort
|
||||
* @property array<int,array<string,string>> $sharing
|
||||
* @property array<string,string> $shortcuts
|
||||
* @property bool $sides_close_article
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Hledat',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Suchen',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Search', // TODO
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Search', // IGNORE
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // IGNORE
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // IGNORE
|
||||
'name_desc' => 'Category, feed titles Z→A', // IGNORE
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // IGNORE
|
||||
'date_desc' => 'Publication date 9→1', // IGNORE
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // IGNORE
|
||||
'name_desc' => 'Feed title Z→A', // IGNORE
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // IGNORE
|
||||
'id_desc' => 'Freshly received first', // IGNORE
|
||||
'link_asc' => 'Link A→Z', // IGNORE
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Search',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria',
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z',
|
||||
'name_desc' => 'Category, feed titles Z→A',
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9',
|
||||
'date_desc' => 'Publication date 9→1',
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z',
|
||||
'name_desc' => 'Feed title Z→A',
|
||||
),
|
||||
'id_asc' => 'Freshly received last',
|
||||
'id_desc' => 'Freshly received first',
|
||||
'link_asc' => 'Link A→Z',
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Buscar',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => ' جستجو',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Haku',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Rechercher',
|
||||
'sort' => array(
|
||||
'_' => 'Critère de tri',
|
||||
'c' => array(
|
||||
'name_asc' => 'Catégorie, flux (titres) A→Z',
|
||||
'name_desc' => 'Catégorie, flux (titres) Z→A',
|
||||
),
|
||||
'date_asc' => 'Date de publication 1→9',
|
||||
'date_desc' => 'Date de publication 9→1',
|
||||
'f' => array(
|
||||
'name_asc' => 'Flux (titre) A→Z',
|
||||
'name_desc' => 'Flux (titre) Z→A',
|
||||
),
|
||||
'id_asc' => 'Reçus récemment en dernier',
|
||||
'id_desc' => 'Reçus récemment en premier',
|
||||
'link_asc' => 'Lien A→Z',
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'חיפוש',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Keresés',
|
||||
'sort' => array(
|
||||
'_' => 'Rendezési sorrend',
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Kiadás dátuma 1→9',
|
||||
'date_desc' => 'Kiadás dátuma 9→1',
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Frissen fogadott utoljára',
|
||||
'id_desc' => 'Frissen fogadott először',
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Cari',
|
||||
'sort' => array(
|
||||
'_' => 'Kriteria pengurutan',
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Tanggal publikasi 1→9',
|
||||
'date_desc' => 'Tanggal publikasi 9→1',
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Yang baru diterima terakhir',
|
||||
'id_desc' => 'Yang baru diterima paling awal',
|
||||
'link_asc' => 'Tautan A→Z',
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Cerca',
|
||||
'sort' => array(
|
||||
'_' => 'Ordina per',
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Data di pubblicazione 1→9',
|
||||
'date_desc' => 'Data di pubblicazione 9→1',
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Dal meno recente',
|
||||
'id_desc' => 'Dal più recente',
|
||||
'link_asc' => 'Link A→Z', // IGNORE
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => '検索',
|
||||
'sort' => array(
|
||||
'_' => '並べ替え',
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => '公開日順 1→9',
|
||||
'date_desc' => '公開日順 9→1',
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => '新着を最後に並べる',
|
||||
'id_desc' => '新着を最初に並べる',
|
||||
'link_asc' => 'リンクURL順 A→Z',
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => '검색',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Meklēt',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Zoeken',
|
||||
'sort' => array(
|
||||
'_' => 'Sorteercriteria',
|
||||
'c' => array(
|
||||
'name_asc' => 'Categorie, feedtitels A→Z',
|
||||
'name_desc' => 'Categorie, feedtitels Z→A',
|
||||
),
|
||||
'date_asc' => 'Publicatiedatum 1→9',
|
||||
'date_desc' => 'Publicatiedatum 9→1',
|
||||
'f' => array(
|
||||
'name_asc' => 'Feedtitel A→Z',
|
||||
'name_desc' => 'Feedtitel Z→A',
|
||||
),
|
||||
'id_asc' => 'Nieuw ontvangen laatst',
|
||||
'id_desc' => 'Nieuw ontvangen eerst',
|
||||
'link_asc' => 'Link A→Z', // IGNORE
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Recercar',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Szukaj',
|
||||
'sort' => array(
|
||||
'_' => 'Kryteria sortowania',
|
||||
'c' => array(
|
||||
'name_asc' => 'Tytuł kategorii i kanału A→Z',
|
||||
'name_desc' => 'Tytuł kategorii i kanału Z→A',
|
||||
),
|
||||
'date_asc' => 'Data publikacji 1→9',
|
||||
'date_desc' => 'Data publikacji 9→1',
|
||||
'f' => array(
|
||||
'name_asc' => 'Tytuł kanału A→Z',
|
||||
'name_desc' => 'Tytuł kanału Z→A',
|
||||
),
|
||||
'id_asc' => 'Najpożniej otrzymane',
|
||||
'id_desc' => 'Najwcześniej otrzymane',
|
||||
'link_asc' => 'Odnośnik A→Z',
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Buscar',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Pesquisar',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Поиск',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Hľadať',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => 'Ara',
|
||||
'sort' => array(
|
||||
'_' => 'Sıralama kriteri',
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Yayın tarihi 1→9',
|
||||
'date_desc' => 'Yayın tarihi 9→1',
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Son alınanlar en sonda',
|
||||
'id_desc' => 'Son alınanlar başta',
|
||||
'link_asc' => 'Bağlantı A→Z',
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => '搜索',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -77,8 +77,16 @@ return array(
|
||||
'search_short' => '搜尋',
|
||||
'sort' => array(
|
||||
'_' => 'Sorting criteria', // TODO
|
||||
'c' => array(
|
||||
'name_asc' => 'Category, feed titles A→Z', // TODO
|
||||
'name_desc' => 'Category, feed titles Z→A', // TODO
|
||||
),
|
||||
'date_asc' => 'Publication date 1→9', // TODO
|
||||
'date_desc' => 'Publication date 9→1', // TODO
|
||||
'f' => array(
|
||||
'name_asc' => 'Feed title A→Z', // TODO
|
||||
'name_desc' => 'Feed title Z→A', // TODO
|
||||
),
|
||||
'id_asc' => 'Freshly received last', // TODO
|
||||
'id_desc' => 'Freshly received first', // TODO
|
||||
'link_asc' => 'Link A→Z', // TODO
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
if (($s = Minz_Request::paramString('state', plaintext: true)) !== '' && ctype_digit($s)) {
|
||||
$state_filter_manual .= '&state=' . $s;
|
||||
}
|
||||
if (($s = Minz_Request::paramString('sort', plaintext: true)) !== '' && ctype_alpha($s)) {
|
||||
if (($s = Minz_Request::paramString('sort', plaintext: true)) !== '' && preg_match('/^[a-z.]+$/', $s)) {
|
||||
$state_filter_manual .= '&sort=' . $s;
|
||||
}
|
||||
if (($s = Minz_Request::paramString('order', plaintext: true)) !== '' && ctype_alpha($s)) {
|
||||
|
||||
@@ -235,6 +235,12 @@
|
||||
<a href="<?= Minz_Url::display($url_order, amend: ['params' => ['sort' => 'link', 'order' => 'DESC']]) ?>"><?= _t('index.menu.sort.link_desc') ?></a></li>
|
||||
<li class="item" role="radio" aria-checked="<?= FreshRSS_Context::$order === 'DESC' && FreshRSS_Context::$sort === 'title' ? 'true' : 'false' ?>">
|
||||
<a href="<?= Minz_Url::display($url_order, amend: ['params' => ['sort' => 'title', 'order' => 'DESC']]) ?>"><?= _t('index.menu.sort.title_desc') ?></a></li>
|
||||
<li class="item" role="radio" aria-checked="<?= FreshRSS_Context::$order === 'DESC' && FreshRSS_Context::$sort === 'f.name' ? 'true' : 'false' ?>">
|
||||
<a href="<?= Minz_Url::display($url_order, amend: ['params' => ['sort' => 'f.name', 'order' => 'DESC']]) ?>"><?= _t('index.menu.sort.f.name_desc') ?></a></li>
|
||||
<li class="item" role="radio" aria-checked="<?= FreshRSS_Context::$order === 'DESC' && FreshRSS_Context::$sort === 'c.name' ? 'true' : 'false' ?>">
|
||||
<a href="<?= Minz_Url::display($url_order, amend: ['params' => ['sort' => 'c.name', 'order' => 'DESC']]) ?>"><?= _t('index.menu.sort.c.name_desc') ?></a></li>
|
||||
<li class="item separator" role="radio" aria-checked="<?= FreshRSS_Context::$sort === 'rand' ? 'true' : 'false' ?>">
|
||||
<a href="<?= Minz_Url::display($url_order, amend: ['params' => ['sort' => 'rand', 'order' => null]]) ?>"><?= _t('index.menu.sort.rand') ?></a></li>
|
||||
<li class="item separator" role="radio" aria-checked="<?= FreshRSS_Context::$order === 'ASC' && FreshRSS_Context::$sort === 'id' ? 'true' : 'false' ?>">
|
||||
<a href="<?= Minz_Url::display($url_order, amend: ['params' => ['sort' => 'id', 'order' => 'ASC']]) ?>"><?= _t('index.menu.sort.id_asc') ?></a></li>
|
||||
<li class="item" role="radio" aria-checked="<?= FreshRSS_Context::$order === 'ASC' && FreshRSS_Context::$sort === 'date' ? 'true' : 'false' ?>">
|
||||
@@ -243,8 +249,10 @@
|
||||
<a href="<?= Minz_Url::display($url_order, amend: ['params' => ['sort' => 'link', 'order' => 'ASC']]) ?>"><?= _t('index.menu.sort.link_asc') ?></a></li>
|
||||
<li class="item" role="radio" aria-checked="<?= FreshRSS_Context::$order === 'ASC' && FreshRSS_Context::$sort === 'title' ? 'true' : 'false' ?>">
|
||||
<a href="<?= Minz_Url::display($url_order, amend: ['params' => ['sort' => 'title', 'order' => 'ASC']]) ?>"><?= _t('index.menu.sort.title_asc') ?></a></li>
|
||||
<li class="item separator" role="radio" aria-checked="<?= FreshRSS_Context::$sort === 'rand' ? 'true' : 'false' ?>">
|
||||
<a href="<?= Minz_Url::display($url_order, amend: ['params' => ['sort' => 'rand', 'order' => null]]) ?>"><?= _t('index.menu.sort.rand') ?></a></li>
|
||||
<li class="item" role="radio" aria-checked="<?= FreshRSS_Context::$order === 'ASC' && FreshRSS_Context::$sort === 'f.name' ? 'true' : 'false' ?>">
|
||||
<a href="<?= Minz_Url::display($url_order, amend: ['params' => ['sort' => 'f.name', 'order' => 'ASC']]) ?>"><?= _t('index.menu.sort.f.name_asc') ?></a></li>
|
||||
<li class="item" role="radio" aria-checked="<?= FreshRSS_Context::$order === 'ASC' && FreshRSS_Context::$sort === 'c.name' ? 'true' : 'false' ?>">
|
||||
<a href="<?= Minz_Url::display($url_order, amend: ['params' => ['sort' => 'c.name', 'order' => 'ASC']]) ?>"><?= _t('index.menu.sort.c.name_asc') ?></a></li>
|
||||
</ul>
|
||||
<a class="dropdown-close" href="#close">❌</a>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user