This repository has been archived on 2025-02-26. You can view files and clone it, but cannot push or open issues or pull requests.
szurubooru/src/Models/SearchParsers/PostSearchParser.php
2014-05-17 00:02:01 +02:00

302 lines
9.9 KiB
PHP

<?php
use \Chibi\Sql as Sql;
class PostSearchParser extends AbstractSearchParser
{
private $tags;
private $showHidden = false;
private $showDisliked = false;
protected function processSetup(&$tokens)
{
$config = Core::getConfig();
$this->tags = [];
$crit = new Sql\ConjunctionFunctor();
$allowedSafety = array_map(
function($safety)
{
return $safety->toInteger();
},
Access::getAllowedSafety());
$crit->add(Sql\InFunctor::fromArray('post.safety', Sql\Binding::fromArray($allowedSafety)));
$this->statement->setCriterion($crit);
if (count($tokens) > $config->browsing->maxSearchTokens)
throw new SimpleException('Too many search tokens (maximum: %d)', $config->browsing->maxSearchTokens);
}
protected function processTeardown()
{
if (Auth::getCurrentUser()->getSettings()->hasEnabledHidingDislikedPosts() and !$this->showDisliked)
$this->processComplexToken('special', 'disliked', true);
if (!Access::check(new Privilege(Privilege::ListPosts, 'hidden')) or !$this->showHidden)
$this->processComplexToken('special', 'hidden', true);
foreach ($this->tags as $item)
{
list ($tagName, $neg) = $item;
$tag = TagModel::getByName($tagName);
$innerStmt = new Sql\SelectStatement();
$innerStmt->setTable('post_tag');
$innerStmt->setCriterion((new Sql\ConjunctionFunctor)
->add(new Sql\EqualsFunctor('post_tag.post_id', 'post.id'))
->add(new Sql\EqualsFunctor('post_tag.tag_id', new Sql\Binding($tag->getId()))));
$operator = new Sql\ExistsFunctor($innerStmt);
if ($neg)
$operator = new Sql\NegationFunctor($operator);
$this->statement->getCriterion()->add($operator);
}
$this->statement->addOrderBy('post.id',
empty($this->statement->getOrderBy())
? Sql\SelectStatement::ORDER_DESC
: $this->statement->getOrderBy()[0][1]);
}
protected function processSimpleToken($value, $neg)
{
$this->tags []= [$value, $neg];
return true;
}
protected function prepareCriterionForComplexToken($key, $value)
{
if (in_array($key, ['id', 'ids']))
{
$ids = preg_split('/[;,]/', $value);
$ids = array_map('intval', $ids);
return Sql\InFunctor::fromArray('post.id', Sql\Binding::fromArray($ids));
}
elseif (in_array($key, ['fav', 'favs', 'favd']))
{
$user = UserModel::getByName($value);
$innerStmt = (new Sql\SelectStatement)
->setTable('favoritee')
->setCriterion((new Sql\ConjunctionFunctor)
->add(new Sql\EqualsFunctor('favoritee.post_id', 'post.id'))
->add(new Sql\EqualsFunctor('favoritee.user_id', new Sql\Binding($user->getId()))));
return new Sql\ExistsFunctor($innerStmt);
}
elseif (in_array($key, ['comment', 'comments', 'commenter', 'commented']))
{
$user = UserModel::getByName($value);
$innerStmt = (new Sql\SelectStatement)
->setTable('comment')
->setCriterion((new Sql\ConjunctionFunctor)
->add(new Sql\EqualsFunctor('comment.post_id', 'post.id'))
->add(new Sql\EqualsFunctor('comment.commenter_id', new Sql\Binding($user->getId()))));
return new Sql\ExistsFunctor($innerStmt);
}
elseif (in_array($key, ['submit', 'upload', 'uploads', 'uploader', 'uploaded']))
{
$user = UserModel::getByName($value);
return new Sql\EqualsFunctor('post.uploader_id', new Sql\Binding($user->getId()));
}
elseif (in_array($key, ['idmin', 'id_min']))
return new Sql\EqualsOrGreaterFunctor('post.id', new Sql\Binding(intval($value)));
elseif (in_array($key, ['idmax', 'id_max']))
return new Sql\EqualsOrLesserFunctor('post.id', new Sql\Binding(intval($value)));
elseif (in_array($key, ['scoremin', 'score_min']))
return new Sql\EqualsOrGreaterFunctor('post.score', new Sql\Binding(intval($value)));
elseif (in_array($key, ['scoremax', 'score_max']))
return new Sql\EqualsOrLesserFunctor('post.score', new Sql\Binding(intval($value)));
elseif (in_array($key, ['tagmin', 'tag_min']))
return new Sql\EqualsOrGreaterFunctor('post.tag_count', new Sql\Binding(intval($value)));
elseif (in_array($key, ['tagmax', 'tag_max']))
return new Sql\EqualsOrLesserFunctor('post.tag_count', new Sql\Binding(intval($value)));
elseif (in_array($key, ['favmin', 'fav_min']))
return new Sql\EqualsOrGreaterFunctor('post.fav_count', new Sql\Binding(intval($value)));
elseif (in_array($key, ['favmax', 'fav_max']))
return new Sql\EqualsOrLesserFunctor('post.fav_count', new Sql\Binding(intval($value)));
elseif (in_array($key, ['commentmin', 'comment_min']))
return new Sql\EqualsOrGreaterFunctor('post.comment_count', new Sql\Binding(intval($value)));
elseif (in_array($key, ['commentmax', 'comment_max']))
return new Sql\EqualsOrLesserFunctor('post.comment_count', new Sql\Binding(intval($value)));
elseif (in_array($key, ['date']))
{
list ($dateMin, $dateMax) = self::parseDate($value);
return (new Sql\ConjunctionFunctor)
->add(new Sql\EqualsOrLesserFunctor('post.upload_date', new Sql\Binding($dateMax)))
->add(new Sql\EqualsOrGreaterFunctor('post.upload_date', new Sql\Binding($dateMin)));
}
elseif (in_array($key, ['datemin', 'date_min']))
{
list ($dateMin, $dateMax) = self::parseDate($value);
return new Sql\EqualsOrGreaterFunctor('post.upload_date', new Sql\Binding($dateMin));
}
elseif (in_array($key, ['datemax', 'date_max']))
{
list ($dateMin, $dateMax) = self::parseDate($value);
return new Sql\EqualsOrLesserFunctor('post.upload_date', new Sql\Binding($dateMax));
}
elseif ($key == 'special')
{
$activeUser = Auth::getCurrentUser();
$value = strtolower($value);
if (in_array($value, ['fav', 'favs', 'favd']))
{
return $this->prepareCriterionForComplexToken('fav', $activeUser->getName());
}
elseif (in_array($value, ['like', 'liked', 'likes']))
{
if (!$this->statement->isTableJoined('post_score'))
{
$this->statement->addLeftOuterJoin('post_score', (new Sql\ConjunctionFunctor)
->add(new Sql\EqualsFunctor('post_score.post_id', 'post.id'))
->add(new Sql\EqualsFunctor('post_score.user_id', new Sql\Binding($activeUser->getId()))));
}
return new Sql\EqualsFunctor(new Sql\IfNullFunctor('post_score.score', '0'), '1');
}
elseif (in_array($value, ['dislike', 'disliked', 'dislikes']))
{
$this->showDisliked = true;
if (!$this->statement->isTableJoined('post_score'))
{
$this->statement->addLeftOuterJoin('post_score', (new Sql\ConjunctionFunctor)
->add(new Sql\EqualsFunctor('post_score.post_id', 'post.id'))
->add(new Sql\EqualsFunctor('post_score.user_id', new Sql\Binding($activeUser->getId()))));
}
return new Sql\EqualsFunctor(new Sql\IfNullFunctor('post_score.score', '0'), '-1');
}
elseif ($value == 'hidden')
{
$this->showHidden = true;
return new Sql\StringExpression('hidden');
}
else
throw new SimpleException('Invalid special token "%s"', $value);
}
elseif ($key == 'type')
{
$value = strtolower($value);
if (in_array($value, ['swf', 'flash']))
$type = PostType::Flash;
elseif (in_array($value, ['img', 'image']))
$type = PostType::Image;
elseif ($value == 'video')
$type = PostType::Video;
elseif ($value == 'yt' or $value == 'youtube')
$type = PostType::Youtube;
else
throw new SimpleException('Invalid post type "%s"', $value);
return new Sql\EqualsFunctor('type', new Sql\Binding($type));
}
return null;
}
protected function processComplexToken($key, $value, $neg)
{
$criterion = $this->prepareCriterionForComplexToken($key, $value);
if (!$criterion)
return false;
if ($neg)
$criterion = new Sql\NegationFunctor($criterion);
$this->statement->getCriterion()->add($criterion);
return true;
}
protected function processOrderToken($orderByString, $orderDir)
{
$randomReset = true;
if (in_array($orderByString, ['id']))
$orderColumn = 'post.id';
elseif (in_array($orderByString, ['date']))
$orderColumn = 'post.upload_date';
elseif (in_array($orderByString, ['score']))
$orderColumn = 'post.score';
elseif (in_array($orderByString, ['comment', 'comments', 'commentcount', 'comment_count']))
$orderColumn = 'post.comment_count';
elseif (in_array($orderByString, ['fav', 'favs', 'favcount', 'fav_count']))
$orderColumn = 'post.fav_count';
elseif (in_array($orderByString, ['tag', 'tags', 'tagcount', 'tag_count']))
$orderColumn = 'post.tag_count';
elseif (in_array($orderByString, ['commentdate', 'comment_date']))
$orderColumn = 'post.comment_date';
elseif (in_array($orderByString, ['favdate', 'fav_date']))
$orderColumn = 'post.fav_date';
elseif (in_array($orderByString, ['filesize', 'file_size']))
$orderColumn = 'post.file_size';
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 = new Sql\SubstrFunctor(
new Sql\MultiplicationFunctor('post.id', $seed),
new Sql\AdditionFunctor(new Sql\LengthFunctor('post.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');
$year = intval($year);
$month = intval($month);
$day = intval($day);
$yearMin = $yearMax = $year;
$monthMin = $monthMax = $month;
$monthMin = $monthMin ?: 1;
$monthMax = $monthMax ?: 12;
$dayMin = $dayMax = $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];
}
}