Search services refactor
Code rerlated to search query parsing moved to separate classes.
This commit is contained in:
parent
4ce4ea6f70
commit
5827626deb
15 changed files with 492 additions and 517 deletions
|
@ -423,7 +423,6 @@ class PostController
|
||||||
PrivilegesHelper::confirmWithException(Privilege::ViewPost);
|
PrivilegesHelper::confirmWithException(Privilege::ViewPost);
|
||||||
PrivilegesHelper::confirmWithException(Privilege::ViewPost, PostSafety::toString($post->safety));
|
PrivilegesHelper::confirmWithException(Privilege::ViewPost, PostSafety::toString($post->safety));
|
||||||
|
|
||||||
PostSearchService::enableTokenLimit(false);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$this->context->transport->lastSearchQuery = InputHelper::get('last-search-query');
|
$this->context->transport->lastSearchQuery = InputHelper::get('last-search-query');
|
||||||
|
@ -439,7 +438,6 @@ class PostController
|
||||||
PostSearchService::getPostIdsAround(
|
PostSearchService::getPostIdsAround(
|
||||||
$this->context->transport->lastSearchQuery, $id);
|
$this->context->transport->lastSearchQuery, $id);
|
||||||
}
|
}
|
||||||
PostSearchService::enableTokenLimit(true);
|
|
||||||
|
|
||||||
$favorite = $this->context->user->hasFavorited($post);
|
$favorite = $this->context->user->hasFavorited($post);
|
||||||
$score = $this->context->user->getScore($post);
|
$score = $this->context->user->getScore($post);
|
||||||
|
|
|
@ -12,8 +12,8 @@ class TagController
|
||||||
public function listAction($filter = null, $page = 1)
|
public function listAction($filter = null, $page = 1)
|
||||||
{
|
{
|
||||||
$this->context->viewName = 'tag-list-wrapper';
|
$this->context->viewName = 'tag-list-wrapper';
|
||||||
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::ListTags);
|
PrivilegesHelper::confirmWithException(Privilege::ListTags);
|
||||||
|
|
||||||
$suppliedFilter = $filter ?: InputHelper::get('filter') ?: 'order:alpha,asc';
|
$suppliedFilter = $filter ?: InputHelper::get('filter') ?: 'order:alpha,asc';
|
||||||
$page = max(1, intval($page));
|
$page = max(1, intval($page));
|
||||||
$tagsPerPage = intval($this->config->browsing->tagsPerPage);
|
$tagsPerPage = intval($this->config->browsing->tagsPerPage);
|
||||||
|
@ -22,6 +22,7 @@ class TagController
|
||||||
$tagCount = TagSearchService::getEntityCount($suppliedFilter);
|
$tagCount = TagSearchService::getEntityCount($suppliedFilter);
|
||||||
$pageCount = ceil($tagCount / $tagsPerPage);
|
$pageCount = ceil($tagCount / $tagsPerPage);
|
||||||
$page = min($pageCount, $page);
|
$page = min($pageCount, $page);
|
||||||
|
|
||||||
$this->context->filter = $suppliedFilter;
|
$this->context->filter = $suppliedFilter;
|
||||||
$this->context->transport->tags = $tags;
|
$this->context->transport->tags = $tags;
|
||||||
|
|
||||||
|
|
|
@ -100,35 +100,32 @@ class UserController
|
||||||
/**
|
/**
|
||||||
* @route /users
|
* @route /users
|
||||||
* @route /users/{page}
|
* @route /users/{page}
|
||||||
* @route /users/{sortStyle}
|
* @route /users/{filter}
|
||||||
* @route /users/{sortStyle}/{page}
|
* @route /users/{filter}/{page}
|
||||||
* @validate sortStyle alpha|alpha,asc|alpha,desc|date,asc|date,desc|pending
|
* @validate filter [a-zA-Z\32:,_-]+
|
||||||
* @validate page [0-9]+
|
* @validate page [0-9]+
|
||||||
*/
|
*/
|
||||||
public function listAction($sortStyle, $page)
|
public function listAction($filter, $page)
|
||||||
{
|
{
|
||||||
if ($sortStyle == '' or $sortStyle == 'alpha')
|
|
||||||
$sortStyle = 'alpha,asc';
|
|
||||||
if ($sortStyle == 'date')
|
|
||||||
$sortStyle = 'date,asc';
|
|
||||||
|
|
||||||
$page = intval($page);
|
|
||||||
$usersPerPage = intval($this->config->browsing->usersPerPage);
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::ListUsers);
|
PrivilegesHelper::confirmWithException(Privilege::ListUsers);
|
||||||
|
|
||||||
$page = max(1, $page);
|
$suppliedFilter = $filter ?: InputHelper::get('filter') ?: 'order:alpha,asc';
|
||||||
$users = UserSearchService::getEntities($sortStyle, $usersPerPage, $page);
|
$page = max(1, intval($page));
|
||||||
$userCount = UserSearchService::getEntityCount($sortStyle);
|
$usersPerPage = intval($this->config->browsing->usersPerPage);
|
||||||
$pageCount = ceil($userCount / $usersPerPage);
|
|
||||||
|
|
||||||
$this->context->sortStyle = $sortStyle;
|
$users = UserSearchService::getEntities($suppliedFilter, $usersPerPage, $page);
|
||||||
|
$userCount = UserSearchService::getEntityCount($suppliedFilter);
|
||||||
|
$pageCount = ceil($userCount / $usersPerPage);
|
||||||
|
$page = min($pageCount, $page);
|
||||||
|
|
||||||
|
$this->context->filter = $suppliedFilter;
|
||||||
|
$this->context->transport->users = $users;
|
||||||
$this->context->transport->paginator = new StdClass;
|
$this->context->transport->paginator = new StdClass;
|
||||||
$this->context->transport->paginator->page = $page;
|
$this->context->transport->paginator->page = $page;
|
||||||
$this->context->transport->paginator->pageCount = $pageCount;
|
$this->context->transport->paginator->pageCount = $pageCount;
|
||||||
$this->context->transport->paginator->entityCount = $userCount;
|
$this->context->transport->paginator->entityCount = $userCount;
|
||||||
$this->context->transport->paginator->entities = $users;
|
$this->context->transport->paginator->entities = $users;
|
||||||
$this->context->transport->paginator->params = func_get_args();
|
$this->context->transport->paginator->params = func_get_args();
|
||||||
$this->context->transport->users = $users;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
99
src/Models/SearchParsers/AbstractSearchParser.php
Normal file
99
src/Models/SearchParsers/AbstractSearchParser.php
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
abstract class AbstractSearchParser
|
||||||
|
{
|
||||||
|
protected $statement;
|
||||||
|
|
||||||
|
public function decorate(SqlSelectStatement $statement, $filterString)
|
||||||
|
{
|
||||||
|
$this->statement = $statement;
|
||||||
|
|
||||||
|
$tokens = preg_split('/\s+/', $filterString);
|
||||||
|
$tokens = array_filter($tokens);
|
||||||
|
$tokens = array_unique($tokens);
|
||||||
|
$this->processSetup($tokens);
|
||||||
|
|
||||||
|
foreach ($tokens as $token)
|
||||||
|
{
|
||||||
|
$neg = false;
|
||||||
|
if ($token{0} == '-')
|
||||||
|
{
|
||||||
|
$token = substr($token, 1);
|
||||||
|
$neg = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($token, ':') !== false)
|
||||||
|
{
|
||||||
|
list ($key, $value) = explode(':', $token, 2);
|
||||||
|
$key = strtolower($key);
|
||||||
|
|
||||||
|
if ($key == 'order')
|
||||||
|
{
|
||||||
|
$this->internalProcessOrderToken($value, $neg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!$this->processComplexToken($key, $value, $neg))
|
||||||
|
throw new SimpleException('Invalid search token: ' . $key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!$this->processSimpleToken($token, $neg))
|
||||||
|
throw new SimpleException('Invalid search token: ' . $token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->processTeardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processSetup(&$tokens)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processTeardown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function internalProcessOrderToken($orderToken, $neg)
|
||||||
|
{
|
||||||
|
$arr = preg_split('/[;,]/', $orderToken);
|
||||||
|
if (count($arr) == 1)
|
||||||
|
$arr []= 'asc';
|
||||||
|
|
||||||
|
if (count($arr) != 2)
|
||||||
|
throw new SimpleException('Invalid search order token: ' . $orderToken);
|
||||||
|
|
||||||
|
$orderByString = strtolower(array_shift($arr));
|
||||||
|
$orderDirString = strtolower(array_shift($arr));
|
||||||
|
if ($orderDirString == 'asc')
|
||||||
|
$orderDir = SqlSelectStatement::ORDER_ASC;
|
||||||
|
elseif ($orderDirString == 'desc')
|
||||||
|
$orderDir = SqlSelectStatement::ORDER_DESC;
|
||||||
|
else
|
||||||
|
throw new SimpleException('Invalid search order direction: ' . $searchOrderDir);
|
||||||
|
|
||||||
|
if ($neg)
|
||||||
|
{
|
||||||
|
$orderDir = $orderDir == SqlSelectStatement::ORDER_ASC
|
||||||
|
? SqlSelectStatement::ORDER_DESC
|
||||||
|
: SqlSelectStatement::ORDER_ASC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->processOrderToken($orderByString, $orderDir))
|
||||||
|
throw new SimpleException('Invalid search order type: ' . $orderbyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processComplexToken($key, $value, $neg)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processSimpleToken($value, $neg)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processOrderToken($orderToken, $orderDir)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
16
src/Models/SearchParsers/CommentSearchParser.php
Normal file
16
src/Models/SearchParsers/CommentSearchParser.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
class CommentSearchParser extends AbstractSearchParser
|
||||||
|
{
|
||||||
|
protected function processSetup(&$tokens)
|
||||||
|
{
|
||||||
|
$this->statement->addInnerJoin('post', new SqlEqualsOperator('post_id', 'post.id'));
|
||||||
|
|
||||||
|
$allowedSafety = PrivilegesHelper::getAllowedSafety();
|
||||||
|
$this->statement->setCriterion(new SqlConjunction());
|
||||||
|
$this->statement->getCriterion()->add(SqlInOperator::fromArray('post.safety', SqlBinding::fromArray($allowedSafety)));
|
||||||
|
if (!PrivilegesHelper::confirm(Privilege::ListPosts, 'hidden'))
|
||||||
|
$this->statement->getCriterion()->add(new SqlNegationOperator(new SqlStringExpression('hidden')));
|
||||||
|
|
||||||
|
$this->statement->addOrderBy('comment.id', SqlSelectStatement::ORDER_DESC);
|
||||||
|
}
|
||||||
|
}
|
258
src/Models/SearchParsers/PostSearchParser.php
Normal file
258
src/Models/SearchParsers/PostSearchParser.php
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
<?php
|
||||||
|
class PostSearchParser extends AbstractSearchParser
|
||||||
|
{
|
||||||
|
private $tags;
|
||||||
|
|
||||||
|
protected function processSetup(&$tokens)
|
||||||
|
{
|
||||||
|
$config = \Chibi\Registry::getConfig();
|
||||||
|
|
||||||
|
$this->tags = [];
|
||||||
|
$this->statement->setCriterion(new SqlConjunction());
|
||||||
|
|
||||||
|
$allowedSafety = PrivilegesHelper::getAllowedSafety();
|
||||||
|
$this->statement->getCriterion()->add(SqlInOperator::fromArray('safety', SqlBinding::fromArray($allowedSafety)));
|
||||||
|
|
||||||
|
if (\Chibi\Registry::getContext()->user->hasEnabledHidingDislikedPosts() and !in_array('special:disliked', array_map('strtolower', $tokens)))
|
||||||
|
$this->processComplexToken('special', 'disliked', true);
|
||||||
|
|
||||||
|
if (!PrivilegesHelper::confirm(Privilege::ListPosts, 'hidden') or !in_array('special:hidden', array_map('strtolower', $tokens)))
|
||||||
|
$this->processComplexToken('special', 'hidden', true);
|
||||||
|
|
||||||
|
if (count($tokens) > $config->browsing->maxSearchTokens)
|
||||||
|
throw new SimpleException('Too many search tokens (maximum: ' . $config->browsing->maxSearchTokens . ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processTeardown()
|
||||||
|
{
|
||||||
|
foreach ($this->tags as $item)
|
||||||
|
{
|
||||||
|
list ($tagName, $neg) = $item;
|
||||||
|
$tag = TagModel::findByName($tagName);
|
||||||
|
$innerStmt = new SqlSelectStatement();
|
||||||
|
$innerStmt->setTable('post_tag');
|
||||||
|
$innerStmt->setCriterion((new SqlConjunction)
|
||||||
|
->add(new SqlEqualsOperator('post_id', 'post.id'))
|
||||||
|
->add(new SqlEqualsOperator('post_tag.tag_id', new SqlBinding($tag->id))));
|
||||||
|
$operator = new SqlExistsOperator($innerStmt);
|
||||||
|
if ($neg)
|
||||||
|
$operator = new SqlNegationOperator($operator);
|
||||||
|
$this->statement->getCriterion()->add($operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->statement->addOrderBy('id',
|
||||||
|
empty($this->statement->getOrderBy())
|
||||||
|
? SqlSelectStatement::ORDER_DESC
|
||||||
|
: $this->statement->getOrderBy()[0][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processSimpleToken($value, $neg)
|
||||||
|
{
|
||||||
|
$this->tags []= [$value, $neg];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function getCriterionForComplexToken($key, $value)
|
||||||
|
{
|
||||||
|
if (in_array($key, ['id']))
|
||||||
|
{
|
||||||
|
$ids = preg_split('/[;,]/', $value);
|
||||||
|
$ids = array_map('intval', $ids);
|
||||||
|
return SqlInOperator::fromArray('id', SqlBinding::fromArray($ids));
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif (in_array($key, ['fav', 'favs']))
|
||||||
|
{
|
||||||
|
$user = UserModel::findByNameOrEmail($value);
|
||||||
|
$innerStmt = (new SqlSelectStatement)
|
||||||
|
->setTable('favoritee')
|
||||||
|
->setCriterion((new SqlConjunction)
|
||||||
|
->add(new SqlEqualsOperator('post_id', 'post.id'))
|
||||||
|
->add(new SqlEqualsOperator('favoritee.user_id', new SqlBinding($user->id))));
|
||||||
|
return new SqlExistsOperator($innerStmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif (in_array($key, ['comment', 'commenter']))
|
||||||
|
{
|
||||||
|
$user = UserModel::findByNameOrEmail($value);
|
||||||
|
$innerStmt = (new SqlSelectStatement)
|
||||||
|
->setTable('comment')
|
||||||
|
->setCriterion((new SqlConjunction)
|
||||||
|
->add(new SqlEqualsOperator('post_id', 'post.id'))
|
||||||
|
->add(new SqlEqualsOperator('commenter_id', new SqlBinding($user->id))));
|
||||||
|
return new SqlExistsOperator($innerStmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif (in_array($key, ['submit', 'upload', 'uploader', 'uploaded']))
|
||||||
|
{
|
||||||
|
$user = UserModel::findByNameOrEmail($value);
|
||||||
|
return new SqlEqualsOperator('uploader_id', new SqlBinding($user->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif (in_array($key, ['idmin']))
|
||||||
|
return new SqlEqualsOrGreaterOperator('id', new SqlBinding(intval($value)));
|
||||||
|
|
||||||
|
elseif (in_array($key, ['idmax']))
|
||||||
|
return new SqlEqualsOrLesserOperator('id', new SqlBinding(intval($value)));
|
||||||
|
|
||||||
|
elseif (in_array($key, ['scoremin']))
|
||||||
|
return new SqlEqualsOrGreaterOperator('score', new SqlBinding(intval($value)));
|
||||||
|
|
||||||
|
elseif (in_array($key, ['scoremax']))
|
||||||
|
return new SqlEqualsOrLesserOperator('score', new SqlBinding(intval($value)));
|
||||||
|
|
||||||
|
elseif (in_array($key, ['tagmin']))
|
||||||
|
return new SqlEqualsOrGreaterOperator('tag_count', new SqlBinding(intval($value)));
|
||||||
|
|
||||||
|
elseif (in_array($key, ['tagmax']))
|
||||||
|
return new SqlEqualsOrLesserOperator('tag_count', new SqlBinding(intval($value)));
|
||||||
|
|
||||||
|
elseif (in_array($key, ['favmin']))
|
||||||
|
return new SqlEqualsOrGreaterOperator('fav_count', new SqlBinding(intval($value)));
|
||||||
|
|
||||||
|
elseif (in_array($key, ['favmax']))
|
||||||
|
return new SqlEqualsOrLesserOperator('fav_count', new SqlBinding(intval($value)));
|
||||||
|
|
||||||
|
elseif (in_array($key, ['commentmin']))
|
||||||
|
return new SqlEqualsOrGreaterOperator('comment_count', new SqlBinding(intval($value)));
|
||||||
|
|
||||||
|
elseif (in_array($key, ['commentmax']))
|
||||||
|
return new SqlEqualsOrLesserOperator('comment_count', new SqlBinding(intval($value)));
|
||||||
|
|
||||||
|
elseif (in_array($key, ['datemin', 'date']))
|
||||||
|
{
|
||||||
|
list ($dateMin, $dateMax) = self::parseDate($value);
|
||||||
|
return new SqlEqualsOrGreaterOperator('upload_date', new SqlBinding($dateMin));
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif (in_array($key, ['datemax', 'date']))
|
||||||
|
{
|
||||||
|
list ($dateMin, $dateMax) = self::parseDate($value);
|
||||||
|
return new SqlEqualsOrLesserOperator('upload_date', new SqlBinding($dateMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif ($key == 'special')
|
||||||
|
{
|
||||||
|
$context = \Chibi\Registry::getContext();
|
||||||
|
$value = strtolower($value);
|
||||||
|
if (in_array($value, ['liked', 'likes']))
|
||||||
|
{
|
||||||
|
$innerStmt = new SqlSelectStatement();
|
||||||
|
$innerStmt->setTable('post_score');
|
||||||
|
$innerStmt->setCriterion((new SqlConjunction)
|
||||||
|
->add(new SqlGreaterOperator('score', '0'))
|
||||||
|
->add(new SqlEqualsOperator('post_id', 'post.id'))
|
||||||
|
->add(new SqlEqualsOperator('user_id', new SqlBinding($context->user->id))));
|
||||||
|
return new SqlExistsOperator($innerStmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif (in_array($value, ['disliked', 'dislikes']))
|
||||||
|
{
|
||||||
|
$innerStmt = new SqlSelectStatement();
|
||||||
|
$innerStmt->setTable('post_score');
|
||||||
|
$innerStmt->setCriterion((new SqlConjunction)
|
||||||
|
->add(new SqlLesserOperator('score', '0'))
|
||||||
|
->add(new SqlEqualsOperator('post_id', 'post.id'))
|
||||||
|
->add(new SqlEqualsOperator('user_id', new SqlBinding($context->user->id))));
|
||||||
|
return new SqlExistsOperator($innerStmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif ($value == 'hidden')
|
||||||
|
return new SqlStringExpression('hidden');
|
||||||
|
|
||||||
|
else
|
||||||
|
throw new SimpleException('Invalid special token: ' . $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif ($key == 'type')
|
||||||
|
{
|
||||||
|
$value = strtolower($value);
|
||||||
|
if ($value == 'swf')
|
||||||
|
$type = PostType::Flash;
|
||||||
|
elseif ($value == 'img')
|
||||||
|
$type = PostType::Image;
|
||||||
|
elseif ($value == 'yt' or $value == 'youtube')
|
||||||
|
$type = PostType::Youtube;
|
||||||
|
else
|
||||||
|
throw new SimpleException('Invalid post type: ' . $value);
|
||||||
|
|
||||||
|
return new SqlEqualsOperator('type', new SqlBinding($type));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processComplexToken($key, $value, $neg)
|
||||||
|
{
|
||||||
|
$criterion = self::getCriterionForComplexToken($key, $value);
|
||||||
|
if (!$criterion)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ($neg)
|
||||||
|
$criterion = new SqlNegationOperator($criterion);
|
||||||
|
|
||||||
|
$this->statement->getCriterion()->add($criterion);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processOrderToken($orderByString, $orderDir)
|
||||||
|
{
|
||||||
|
$randomReset = true;
|
||||||
|
|
||||||
|
if (in_array($orderByString, ['id']))
|
||||||
|
$orderColumn = 'id';
|
||||||
|
|
||||||
|
elseif (in_array($orderByString, ['date']))
|
||||||
|
$orderColumn = 'upload_date';
|
||||||
|
|
||||||
|
elseif (in_array($orderByString, ['comment', 'comments', 'commentcount', 'comment_count']))
|
||||||
|
$orderColumn = 'comment_count';
|
||||||
|
|
||||||
|
elseif (in_array($orderByString, ['fav', 'favs', 'favcount', 'fav_count']))
|
||||||
|
$orderColumn = 'fav_count';
|
||||||
|
|
||||||
|
elseif (in_array($orderByString, ['score']))
|
||||||
|
$orderColumn = 'score';
|
||||||
|
|
||||||
|
elseif (in_array($orderByString, ['tag', 'tags', 'tagcount', 'tag_count']))
|
||||||
|
$orderColumn = 'tag_count';
|
||||||
|
|
||||||
|
elseif ($orderByString == 'random')
|
||||||
|
{
|
||||||
|
//seeding works like this: if you visit anything
|
||||||
|
//that triggers order other than random, the seed
|
||||||
|
//is going to reset. however, it stays the same as
|
||||||
|
//long as you keep visiting pages with order:random
|
||||||
|
//specified.
|
||||||
|
$randomReset = false;
|
||||||
|
if (!isset($_SESSION['browsing-seed']))
|
||||||
|
$_SESSION['browsing-seed'] = mt_rand();
|
||||||
|
$seed = $_SESSION['browsing-seed'];
|
||||||
|
$orderColumn = 'SUBSTR(id * ' . $seed .', LENGTH(id) + 2)';
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ($randomReset and isset($_SESSION['browsing-seed']))
|
||||||
|
unset($_SESSION['browsing-seed']);
|
||||||
|
|
||||||
|
$this->statement->setOrderBy($orderColumn, $orderDir);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function parseDate($value)
|
||||||
|
{
|
||||||
|
list ($year, $month, $day) = explode('-', $value . '-0-0');
|
||||||
|
$yearMin = $yearMax = intval($year);
|
||||||
|
$monthMin = $monthMax = intval($month);
|
||||||
|
$monthMin = $monthMin ?: 1;
|
||||||
|
$monthMax = $monthMax ?: 12;
|
||||||
|
$dayMin = $dayMax = intval($day);
|
||||||
|
$dayMin = $dayMin ?: 1;
|
||||||
|
$dayMax = $dayMax ?: intval(date('t', mktime(0, 0, 0, $monthMax, 1, $year)));
|
||||||
|
$timeMin = mktime(0, 0, 0, $monthMin, $dayMin, $yearMin);
|
||||||
|
$timeMax = mktime(0, 0, -1, $monthMax, $dayMax+1, $yearMax);
|
||||||
|
return [$timeMin, $timeMax];
|
||||||
|
}
|
||||||
|
}
|
37
src/Models/SearchParsers/TagSearchParser.php
Normal file
37
src/Models/SearchParsers/TagSearchParser.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
class TagSearchParser extends AbstractSearchParser
|
||||||
|
{
|
||||||
|
protected function processSetup(&$tokens)
|
||||||
|
{
|
||||||
|
$allowedSafety = PrivilegesHelper::getAllowedSafety();
|
||||||
|
$this->statement
|
||||||
|
->addInnerJoin('post_tag', new SqlEqualsOperator('tag.id', 'post_tag.tag_id'))
|
||||||
|
->addInnerJoin('post', new SqlEqualsOperator('post.id', 'post_tag.post_id'))
|
||||||
|
->setCriterion((new SqlConjunction)->add(SqlInOperator::fromArray('safety', SqlBinding::fromArray($allowedSafety))))
|
||||||
|
->groupBy('tag.id');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processSimpleToken($value, $neg)
|
||||||
|
{
|
||||||
|
if ($neg)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (strlen($value) >= 3)
|
||||||
|
$value = '%' . $value;
|
||||||
|
$value .= '%';
|
||||||
|
|
||||||
|
$this->statement->getCriterion()->add(new SqlNoCaseOperator(new SqlLikeOperator('tag.name', new SqlBinding($value))));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processOrderToken($orderByString, $orderDir)
|
||||||
|
{
|
||||||
|
if ($orderByString == 'popularity')
|
||||||
|
$this->statement->setOrderBy('post_count', $orderDir);
|
||||||
|
elseif ($orderByString == 'alpha')
|
||||||
|
$this->statement->setOrderBy('tag.name', $orderDir);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
30
src/Models/SearchParsers/UserSearchParser.php
Normal file
30
src/Models/SearchParsers/UserSearchParser.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
class UserSearchParser extends AbstractSearchParser
|
||||||
|
{
|
||||||
|
protected function processSimpleToken($value, $neg)
|
||||||
|
{
|
||||||
|
if ($neg)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ($value == 'pending')
|
||||||
|
{
|
||||||
|
$this->statement->setCriterion((new SqlDisjunction)
|
||||||
|
->add(new SqlIsNullOperator('staff_confirmed'))
|
||||||
|
->add(new SqlEqualsOperator('staff_confirmed', '0')));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processOrderToken($orderByString, $orderDir)
|
||||||
|
{
|
||||||
|
if ($orderByString == 'alpha')
|
||||||
|
$this->statement->setOrderBy(new SqlNoCaseOperator('name'), $orderDir);
|
||||||
|
elseif ($orderByString == 'date')
|
||||||
|
$this->statement->setOrderBy('join_date', $orderDir);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,9 +8,21 @@ abstract class AbstractSearchService
|
||||||
return $modelClassName;
|
return $modelClassName;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function decorate(SqlSelectStatement $stmt, $searchQuery)
|
protected static function getParserClassName()
|
||||||
|
{
|
||||||
|
$searchServiceClassName = get_called_class();
|
||||||
|
$parserClassName = str_replace('SearchService', 'SearchParser', $searchServiceClassName);
|
||||||
|
return $parserClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function decorateParser(SqlSelectStatement $stmt, $searchQuery)
|
||||||
|
{
|
||||||
|
$parserClassName = self::getParserClassName();
|
||||||
|
(new $parserClassName)->decorate($stmt, $searchQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function decorateCustom(SqlSelectStatement $stmt)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function decoratePager(SqlSelectStatement $stmt, $perPage, $page)
|
protected static function decoratePager(SqlSelectStatement $stmt, $perPage, $page)
|
||||||
|
@ -29,11 +41,12 @@ abstract class AbstractSearchService
|
||||||
|
|
||||||
$stmt = new SqlSelectStatement();
|
$stmt = new SqlSelectStatement();
|
||||||
$stmt->setColumn($table . '.*');
|
$stmt->setColumn($table . '.*');
|
||||||
static::decorate($stmt, $searchQuery);
|
$stmt->setTable($table);
|
||||||
|
static::decorateParser($stmt, $searchQuery);
|
||||||
|
static::decorateCustom($stmt);
|
||||||
static::decoratePager($stmt, $perPage, $page);
|
static::decoratePager($stmt, $perPage, $page);
|
||||||
|
|
||||||
$rows = Database::fetchAll($stmt);
|
return Database::fetchAll($stmt);
|
||||||
return $rows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getEntities($searchQuery, $perPage = null, $page = 1)
|
public static function getEntities($searchQuery, $perPage = null, $page = 1)
|
||||||
|
@ -49,7 +62,9 @@ abstract class AbstractSearchService
|
||||||
$table = $modelClassName::getTableName();
|
$table = $modelClassName::getTableName();
|
||||||
|
|
||||||
$innerStmt = new SqlSelectStatement();
|
$innerStmt = new SqlSelectStatement();
|
||||||
static::decorate($innerStmt, $searchQuery);
|
$innerStmt->setTable($table);
|
||||||
|
static::decorateParser($innerStmt, $searchQuery);
|
||||||
|
static::decorateCustom($innerStmt);
|
||||||
|
|
||||||
$stmt = new SqlSelectStatement();
|
$stmt = new SqlSelectStatement();
|
||||||
$stmt->setColumn(new SqlAliasOperator(new SqlCountOperator('1'), 'count'));
|
$stmt->setColumn(new SqlAliasOperator(new SqlCountOperator('1'), 'count'));
|
||||||
|
|
|
@ -1,17 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
class CommentSearchService extends AbstractSearchService
|
class CommentSearchService extends AbstractSearchService
|
||||||
{
|
{
|
||||||
public static function decorate(SqlSelectStatement $stmt, $searchQuery)
|
|
||||||
{
|
|
||||||
$stmt->setTable('comment');
|
|
||||||
$stmt->addInnerJoin('post', new SqlEqualsOperator('post_id', 'post.id'));
|
|
||||||
|
|
||||||
$allowedSafety = PrivilegesHelper::getAllowedSafety();
|
|
||||||
$stmt->setCriterion(new SqlConjunction());
|
|
||||||
$stmt->getCriterion()->add(SqlInOperator::fromArray('post.safety', SqlBinding::fromArray($allowedSafety)));
|
|
||||||
if (!PrivilegesHelper::confirm(Privilege::ListPosts, 'hidden'))
|
|
||||||
$stmt->getCriterion()->add(new SqlNegationOperator(new SqlStringExpression('hidden')));
|
|
||||||
|
|
||||||
$stmt->addOrderBy('comment.id', SqlSelectStatement::ORDER_DESC);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
class PostSearchService extends AbstractSearchService
|
class PostSearchService extends AbstractSearchService
|
||||||
{
|
{
|
||||||
private static $enableTokenLimit = true;
|
|
||||||
|
|
||||||
public static function getPostIdsAround($searchQuery, $postId)
|
public static function getPostIdsAround($searchQuery, $postId)
|
||||||
{
|
{
|
||||||
return Database::transaction(function() use ($searchQuery, $postId)
|
return Database::transaction(function() use ($searchQuery, $postId)
|
||||||
|
@ -16,7 +14,8 @@ class PostSearchService extends AbstractSearchService
|
||||||
|
|
||||||
$innerStmt = new SqlSelectStatement($searchQuery);
|
$innerStmt = new SqlSelectStatement($searchQuery);
|
||||||
$innerStmt->setColumn('id');
|
$innerStmt->setColumn('id');
|
||||||
self::decorate($innerStmt, $searchQuery);
|
$innerStmt->setTable('post');
|
||||||
|
self::decorateParser($innerStmt, $searchQuery);
|
||||||
$stmt = new SqlInsertStatement();
|
$stmt = new SqlInsertStatement();
|
||||||
$stmt->setTable('post_search');
|
$stmt->setTable('post_search');
|
||||||
$stmt->setSource(['post_id'], $innerStmt);
|
$stmt->setSource(['post_id'], $innerStmt);
|
||||||
|
@ -45,372 +44,4 @@ class PostSearchService extends AbstractSearchService
|
||||||
return [$prevPostId, $nextPostId];
|
return [$prevPostId, $nextPostId];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function enableTokenLimit($enable)
|
|
||||||
{
|
|
||||||
self::$enableTokenLimit = $enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function decorateNegation(SqlExpression $criterion, $negative)
|
|
||||||
{
|
|
||||||
return !$negative
|
|
||||||
? $criterion
|
|
||||||
: new SqlNegationOperator($criterion);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterUserSafety(SqlSelectStatement $stmt)
|
|
||||||
{
|
|
||||||
$allowedSafety = PrivilegesHelper::getAllowedSafety();
|
|
||||||
$stmt->getCriterion()->add(SqlInOperator::fromArray('safety', SqlBinding::fromArray($allowedSafety)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTag(SqlSelectStatement $stmt, $val, $neg)
|
|
||||||
{
|
|
||||||
$tag = TagModel::findByName($val);
|
|
||||||
$innerStmt = new SqlSelectStatement();
|
|
||||||
$innerStmt->setTable('post_tag');
|
|
||||||
$innerStmt->setCriterion((new SqlConjunction)
|
|
||||||
->add(new SqlEqualsOperator('post_id', 'post.id'))
|
|
||||||
->add(new SqlEqualsOperator('post_tag.tag_id', new SqlBinding($tag->id))));
|
|
||||||
$stmt->getCriterion()->add(self::decorateNegation(new SqlExistsOperator($innerStmt), $neg));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenId($val)
|
|
||||||
{
|
|
||||||
$ids = preg_split('/[;,]/', $val);
|
|
||||||
$ids = array_map('intval', $ids);
|
|
||||||
return SqlInOperator::fromArray('id', $ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenIdMin($val)
|
|
||||||
{
|
|
||||||
return new SqlEqualsOrGreaterOperator('id', new SqlBinding(intval($val)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenIdMax($val)
|
|
||||||
{
|
|
||||||
return new SqlEqualsOrLesserOperator('id', new SqlBinding(intval($val)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenScoreMin($val)
|
|
||||||
{
|
|
||||||
return new SqlEqualsOrGreaterOperator('score', new SqlBinding(intval($val)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenScoreMax($val)
|
|
||||||
{
|
|
||||||
return new SqlEqualsOrLesserOperator('score', new SqlBinding(intval($val)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenTagMin($val)
|
|
||||||
{
|
|
||||||
return new SqlEqualsOrGreaterOperator('tag_count', new SqlBinding(intval($val)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenTagMax($val)
|
|
||||||
{
|
|
||||||
return new SqlEqualsOrLesserOperator('tag_count', new SqlBinding(intval($val)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenFavMin($val)
|
|
||||||
{
|
|
||||||
return new SqlEqualsOrGreaterOperator('fav_count', new SqlBinding(intval($val)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenFavMax($val)
|
|
||||||
{
|
|
||||||
return new SqlEqualsOrLesserOperator('fav_count', new SqlBinding(intval($val)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenCommentMin($val)
|
|
||||||
{
|
|
||||||
return new SqlEqualsOrGreaterOperator('comment_count', new SqlBinding(intval($val)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenCommentMax($val)
|
|
||||||
{
|
|
||||||
return new SqlEqualsOrLesserOperator('comment_count', new SqlBinding(intval($val)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenSpecial($val)
|
|
||||||
{
|
|
||||||
$context = \Chibi\Registry::getContext();
|
|
||||||
|
|
||||||
switch ($val)
|
|
||||||
{
|
|
||||||
case 'liked':
|
|
||||||
case 'likes':
|
|
||||||
$innerStmt = new SqlSelectStatement();
|
|
||||||
$innerStmt->setTable('post_score');
|
|
||||||
$innerStmt->setCriterion((new SqlConjunction)
|
|
||||||
->add(new SqlGreaterOperator('score', '0'))
|
|
||||||
->add(new SqlEqualsOperator('post_id', 'post.id'))
|
|
||||||
->add(new SqlEqualsOperator('user_id', new SqlBinding($context->user->id))));
|
|
||||||
return new SqlExistsOperator($innerStmt);
|
|
||||||
|
|
||||||
case 'disliked':
|
|
||||||
case 'dislikes':
|
|
||||||
$innerStmt = new SqlSelectStatement();
|
|
||||||
$innerStmt->setTable('post_score');
|
|
||||||
$innerStmt->setCriterion((new SqlConjunction)
|
|
||||||
->add(new SqlLesserOperator('score', '0'))
|
|
||||||
->add(new SqlEqualsOperator('post_id', 'post.id'))
|
|
||||||
->add(new SqlEqualsOperator('user_id', new SqlBinding($context->user->id))));
|
|
||||||
return new SqlExistsOperator($innerStmt);
|
|
||||||
|
|
||||||
case 'hidden':
|
|
||||||
return new SqlStringExpression('hidden');
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new SimpleException('Unknown special "' . $val . '"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenType($val)
|
|
||||||
{
|
|
||||||
switch ($val)
|
|
||||||
{
|
|
||||||
case 'swf':
|
|
||||||
$type = PostType::Flash;
|
|
||||||
break;
|
|
||||||
case 'img':
|
|
||||||
$type = PostType::Image;
|
|
||||||
break;
|
|
||||||
case 'yt':
|
|
||||||
case 'youtube':
|
|
||||||
$type = PostType::Youtube;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new SimpleException('Unknown type "' . $val . '"');
|
|
||||||
}
|
|
||||||
return new SqlEqualsOperator('type', new SqlBinding($type));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function __filterTokenDateParser($val)
|
|
||||||
{
|
|
||||||
list ($year, $month, $day) = explode('-', $val . '-0-0');
|
|
||||||
$yearMin = $yearMax = intval($year);
|
|
||||||
$monthMin = $monthMax = intval($month);
|
|
||||||
$monthMin = $monthMin ?: 1;
|
|
||||||
$monthMax = $monthMax ?: 12;
|
|
||||||
$dayMin = $dayMax = intval($day);
|
|
||||||
$dayMin = $dayMin ?: 1;
|
|
||||||
$dayMax = $dayMax ?: intval(date('t', mktime(0, 0, 0, $monthMax, 1, $year)));
|
|
||||||
$timeMin = mktime(0, 0, 0, $monthMin, $dayMin, $yearMin);
|
|
||||||
$timeMax = mktime(0, 0, -1, $monthMax, $dayMax+1, $yearMax);
|
|
||||||
return [$timeMin, $timeMax];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenDate($val)
|
|
||||||
{
|
|
||||||
list ($timeMin, $timeMax) = self::__filterTokenDateParser($val);
|
|
||||||
return (new SqlConjunction)
|
|
||||||
->add(new SqlEqualsOrGreaterOperator('upload_date', new SqlBinding($timeMin)))
|
|
||||||
->add(new SqlEqualsOrLesserOperator('upload_date', new SqlBinding($timeMax)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenDateMin($val)
|
|
||||||
{
|
|
||||||
list ($timeMin, $timeMax) = self::__filterTokenDateParser($val);
|
|
||||||
return new SqlEqualsOrGreaterOperator('upload_date', new SqlBinding($timeMin));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenDateMax($val)
|
|
||||||
{
|
|
||||||
list ($timeMin, $timeMax) = self::__filterTokenDateParser($val);
|
|
||||||
return new SqlEqualsOrLesserOperator('upload_date', new SqlBinding($timeMax));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenFav($val)
|
|
||||||
{
|
|
||||||
$user = UserModel::findByNameOrEmail($val);
|
|
||||||
$innerStmt = (new SqlSelectStatement)
|
|
||||||
->setTable('favoritee')
|
|
||||||
->setCriterion((new SqlConjunction)
|
|
||||||
->add(new SqlEqualsOperator('post_id', 'post.id'))
|
|
||||||
->add(new SqlEqualsOperator('favoritee.user_id', new SqlBinding($user->id))));
|
|
||||||
return new SqlExistsOperator($innerStmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenFavs($val)
|
|
||||||
{
|
|
||||||
return self::filterTokenFav($val);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenComment($val)
|
|
||||||
{
|
|
||||||
$user = UserModel::findByNameOrEmail($val);
|
|
||||||
$innerStmt = (new SqlSelectStatement)
|
|
||||||
->setTable('comment')
|
|
||||||
->setCriterion((new SqlConjunction)
|
|
||||||
->add(new SqlEqualsOperator('post_id', 'post.id'))
|
|
||||||
->add(new SqlEqualsOperator('commenter_id', new SqlBinding($user->id))));
|
|
||||||
return new SqlExistsOperator($innerStmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenCommenter($val)
|
|
||||||
{
|
|
||||||
return self::filterTokenComment($searchContext, $stmt, $val);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenSubmit($val)
|
|
||||||
{
|
|
||||||
$user = UserModel::findByNameOrEmail($val);
|
|
||||||
return new SqlEqualsOperator('uploader_id', new SqlBinding($user->id));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenUploader($val)
|
|
||||||
{
|
|
||||||
return self::filterTokenSubmit($val);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenUpload($val)
|
|
||||||
{
|
|
||||||
return self::filterTokenSubmit($val);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function filterTokenUploaded($val)
|
|
||||||
{
|
|
||||||
return self::filterTokenSubmit($val);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected static function changeOrder($stmt, $val, $neg = true)
|
|
||||||
{
|
|
||||||
$randomReset = true;
|
|
||||||
|
|
||||||
$orderDir = SqlSelectStatement::ORDER_DESC;
|
|
||||||
if (substr($val, -4) == 'desc')
|
|
||||||
{
|
|
||||||
$orderDir = SqlSelectStatement::ORDER_DESC;
|
|
||||||
$val = rtrim(substr($val, 0, -4), ',');
|
|
||||||
}
|
|
||||||
elseif (substr($val, -3) == 'asc')
|
|
||||||
{
|
|
||||||
$orderDir = SqlSelectStatement::ORDER_ASC;
|
|
||||||
$val = rtrim(substr($val, 0, -3), ',');
|
|
||||||
}
|
|
||||||
if ($neg)
|
|
||||||
{
|
|
||||||
$orderDir = $orderDir == SqlSelectStatement::ORDER_DESC
|
|
||||||
? SqlSelectStatement::ORDER_ASC
|
|
||||||
: SqlSelectStatement::ORDER_DESC;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($val)
|
|
||||||
{
|
|
||||||
case 'id':
|
|
||||||
$orderColumn = 'id';
|
|
||||||
break;
|
|
||||||
case 'date':
|
|
||||||
$orderColumn = 'upload_date';
|
|
||||||
break;
|
|
||||||
case 'comment':
|
|
||||||
case 'comments':
|
|
||||||
case 'commentcount':
|
|
||||||
case 'comment_count':
|
|
||||||
$orderColumn = 'comment_count';
|
|
||||||
break;
|
|
||||||
case 'fav':
|
|
||||||
case 'favs':
|
|
||||||
case 'favcount':
|
|
||||||
case 'fav_count':
|
|
||||||
$orderColumn = 'fav_count';
|
|
||||||
break;
|
|
||||||
case 'score':
|
|
||||||
$orderColumn = 'score';
|
|
||||||
break;
|
|
||||||
case 'tag':
|
|
||||||
case 'tags':
|
|
||||||
case 'tagcount':
|
|
||||||
case 'tag_count':
|
|
||||||
$orderColumn = 'tag_count';
|
|
||||||
break;
|
|
||||||
case 'random':
|
|
||||||
//seeding works like this: if you visit anything
|
|
||||||
//that triggers order other than random, the seed
|
|
||||||
//is going to reset. however, it stays the same as
|
|
||||||
//long as you keep visiting pages with order:random
|
|
||||||
//specified.
|
|
||||||
$randomReset = false;
|
|
||||||
if (!isset($_SESSION['browsing-seed']))
|
|
||||||
$_SESSION['browsing-seed'] = mt_rand();
|
|
||||||
$seed = $_SESSION['browsing-seed'];
|
|
||||||
$orderColumn = 'SUBSTR(id * ' . $seed .', LENGTH(id) + 2)';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new SimpleException('Unknown key "' . $val . '"');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($randomReset and isset($_SESSION['browsing-seed']))
|
|
||||||
unset($_SESSION['browsing-seed']);
|
|
||||||
|
|
||||||
$stmt->setOrderBy($orderColumn, $orderDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static function decorate(SqlSelectStatement $stmt, $searchQuery)
|
|
||||||
{
|
|
||||||
$config = \Chibi\Registry::getConfig();
|
|
||||||
|
|
||||||
$stmt->setTable('post');
|
|
||||||
$stmt->setCriterion(new SqlConjunction());
|
|
||||||
|
|
||||||
self::filterUserSafety($stmt);
|
|
||||||
|
|
||||||
/* query tokens */
|
|
||||||
$tokens = array_filter(array_unique(preg_split('/\s+/', strtolower($searchQuery))));
|
|
||||||
if (self::$enableTokenLimit and count($tokens) > $config->browsing->maxSearchTokens)
|
|
||||||
throw new SimpleException('Too many search tokens (maximum: ' . $config->browsing->maxSearchTokens . ')');
|
|
||||||
|
|
||||||
if (\Chibi\Registry::getContext()->user->hasEnabledHidingDislikedPosts() and !in_array('special:disliked', $tokens))
|
|
||||||
$tokens []= '-special:disliked';
|
|
||||||
if (!PrivilegesHelper::confirm(Privilege::ListPosts, 'hidden') or !in_array('special:hidden', $tokens))
|
|
||||||
$tokens []= '-special:hidden';
|
|
||||||
|
|
||||||
$searchContext = new StdClass;
|
|
||||||
$searchContext->orderColumn = 'id';
|
|
||||||
$searchContext->orderDir = 1;
|
|
||||||
|
|
||||||
foreach ($tokens as $token)
|
|
||||||
{
|
|
||||||
$neg = false;
|
|
||||||
if ($token{0} == '-')
|
|
||||||
{
|
|
||||||
$neg = true;
|
|
||||||
$token = substr($token, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strpos($token, ':') !== false)
|
|
||||||
{
|
|
||||||
list ($key, $val) = explode(':', $token);
|
|
||||||
$key = strtolower($key);
|
|
||||||
if ($key == 'order')
|
|
||||||
{
|
|
||||||
self::changeOrder($stmt, $val, $neg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$methodName = 'filterToken' . TextHelper::kebabCaseToCamelCase($key);
|
|
||||||
if (!method_exists(__CLASS__, $methodName))
|
|
||||||
throw new SimpleException('Unknown search token "' . $key . '"');
|
|
||||||
|
|
||||||
$criterion = self::$methodName($val);
|
|
||||||
$criterion = self::decorateNegation($criterion, $neg);
|
|
||||||
$stmt->getCriterion()->add($criterion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self::filterTag($stmt, $token, $neg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt->addOrderBy('id',
|
|
||||||
empty($stmt->getOrderBy())
|
|
||||||
? SqlSelectStatement::ORDER_DESC
|
|
||||||
: $stmt->getOrderBy()[0][1]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,74 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
class TagSearchService extends AbstractSearchService
|
class TagSearchService extends AbstractSearchService
|
||||||
{
|
{
|
||||||
public static function decorate(SqlSelectStatement $stmt, $searchQuery)
|
public static function decorateCustom(SqlSelectStatement $stmt)
|
||||||
{
|
{
|
||||||
$allowedSafety = PrivilegesHelper::getAllowedSafety();
|
$stmt->addColumn(new SqlAliasOperator(new SqlCountOperator('post_tag.post_id'), 'post_count'));
|
||||||
$stmt
|
|
||||||
->addColumn('COUNT(post_tag.post_id) AS post_count')
|
|
||||||
->setTable('tag')
|
|
||||||
->addInnerJoin('post_tag', new SqlEqualsOperator('tag.id', 'post_tag.tag_id'))
|
|
||||||
->addInnerJoin('post', new SqlEqualsOperator('post.id', 'post_tag.post_id'));
|
|
||||||
$stmt->setCriterion((new SqlConjunction)->add(SqlInOperator::fromArray('safety', SqlBinding::fromArray($allowedSafety))));
|
|
||||||
|
|
||||||
$orderToken = null;
|
|
||||||
|
|
||||||
if ($searchQuery !== null)
|
|
||||||
{
|
|
||||||
$tokens = preg_split('/\s+/', $searchQuery);
|
|
||||||
foreach ($tokens as $token)
|
|
||||||
{
|
|
||||||
if (strpos($token, ':') !== false)
|
|
||||||
{
|
|
||||||
list ($key, $value) = explode(':', $token);
|
|
||||||
|
|
||||||
if ($key == 'order')
|
|
||||||
$orderToken = $value;
|
|
||||||
else
|
|
||||||
throw new SimpleException('Unknown key: ' . $key);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (strlen($token) >= 3)
|
|
||||||
$token = '%' . $token;
|
|
||||||
$token .= '%';
|
|
||||||
$stmt->getCriterion()->add(new SqlNoCaseOperator(new SqlLikeOperator('tag.name', new SqlBinding($token))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt->groupBy('tag.id');
|
|
||||||
if ($orderToken)
|
|
||||||
self::order($stmt,$orderToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function order(SqlSelectStatement $stmt, $value)
|
|
||||||
{
|
|
||||||
if (strpos($value, ',') !== false)
|
|
||||||
{
|
|
||||||
list ($orderColumn, $orderDir) = explode(',', $value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$orderColumn = $value;
|
|
||||||
$orderDir = 'asc';
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($orderColumn)
|
|
||||||
{
|
|
||||||
case 'popularity':
|
|
||||||
$stmt->setOrderBy('post_count',
|
|
||||||
$orderDir == 'asc'
|
|
||||||
? SqlSelectStatement::ORDER_ASC
|
|
||||||
: SqlSelectStatement::ORDER_DESC);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'alpha':
|
|
||||||
$stmt->setOrderBy(new SqlNoCaseOperator('tag.name'),
|
|
||||||
$orderDir == 'asc'
|
|
||||||
? SqlSelectStatement::ORDER_ASC
|
|
||||||
: SqlSelectStatement::ORDER_DESC);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
class UserSearchService extends AbstractSearchService
|
class UserSearchService extends AbstractSearchService
|
||||||
{
|
{
|
||||||
protected static function decorate(SqlSelectStatement $stmt, $searchQuery)
|
|
||||||
{
|
|
||||||
$stmt->setTable('user');
|
|
||||||
|
|
||||||
$sortStyle = $searchQuery;
|
|
||||||
switch ($sortStyle)
|
|
||||||
{
|
|
||||||
case 'alpha,asc':
|
|
||||||
$stmt->setOrderBy(new SqlNoCaseOperator('name'), SqlSelectStatement::ORDER_ASC);
|
|
||||||
break;
|
|
||||||
case 'alpha,desc':
|
|
||||||
$stmt->setOrderBy(new SqlNoCaseOperator('name'), SqlSelectStatement::ORDER_DESC);
|
|
||||||
break;
|
|
||||||
case 'date,asc':
|
|
||||||
$stmt->setOrderBy('join_date', SqlSelectStatement::ORDER_ASC);
|
|
||||||
break;
|
|
||||||
case 'date,desc':
|
|
||||||
$stmt->setOrderBy('join_date', SqlSelectStatement::ORDER_DESC);
|
|
||||||
break;
|
|
||||||
case 'pending':
|
|
||||||
$stmt->setCriterion((new SqlDisjunction)
|
|
||||||
->add(new SqlIsNullOperator('staff_confirmed'))
|
|
||||||
->add(new SqlEqualsOperator('staff_confirmed', '0')));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new SimpleException('Unknown sort style "' . $sortStyle . '"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<nav class="sort-styles">
|
<nav class="sort-styles">
|
||||||
<ul>
|
<ul>
|
||||||
<?php
|
<?php
|
||||||
$sortStyles =
|
$filters =
|
||||||
[
|
[
|
||||||
'order:alpha,asc' => 'Sort A→Z',
|
'order:alpha,asc' => 'Sort A→Z',
|
||||||
'order:alpha,desc' => 'Sort Z→A',
|
'order:alpha,desc' => 'Sort Z→A',
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
$sortStyles['pending'] = 'Pending staff review';
|
$sortStyles['pending'] = 'Pending staff review';
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<?php foreach ($sortStyles as $key => $text): ?>
|
<?php foreach ($filters as $key => $text): ?>
|
||||||
<?php if ($this->context->filter == $key): ?>
|
<?php if ($this->context->filter == $key): ?>
|
||||||
<li class="active">
|
<li class="active">
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
|
|
|
@ -9,25 +9,25 @@ if ($this->context->user->hasEnabledEndlessScrolling())
|
||||||
<nav class="sort-styles">
|
<nav class="sort-styles">
|
||||||
<ul>
|
<ul>
|
||||||
<?php
|
<?php
|
||||||
$sortStyles =
|
$filters =
|
||||||
[
|
[
|
||||||
'alpha,asc' => 'Sort A→Z',
|
'order:alpha,asc' => 'Sort A→Z',
|
||||||
'alpha,desc' => 'Sort Z→A',
|
'order:alpha,desc' => 'Sort Z→A',
|
||||||
'date,asc' => 'Sort old→new',
|
'order:date,asc' => 'Sort old→new',
|
||||||
'date,desc' => 'Sort new→old',
|
'order:date,desc' => 'Sort new→old',
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($this->config->registration->staffActivation)
|
if ($this->config->registration->staffActivation)
|
||||||
$sortStyles['pending'] = 'Pending staff review';
|
$filters['pending'] = 'Pending staff review';
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<?php foreach ($sortStyles as $key => $text): ?>
|
<?php foreach ($filters as $key => $text): ?>
|
||||||
<?php if ($this->context->sortStyle == $key): ?>
|
<?php if ($this->context->filter == $key): ?>
|
||||||
<li class="active">
|
<li class="active">
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<li>
|
<li>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<a href="<?php echo \Chibi\UrlHelper::route('user', 'list', ['sortStyle' => $key]) ?>"><?php echo $text ?></a>
|
<a href="<?php echo \Chibi\UrlHelper::route('user', 'list', ['filter' => $key]) ?>"><?php echo $text ?></a>
|
||||||
</li>
|
</li>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
Loading…
Reference in a new issue