diff --git a/public_html/media/js/core.js b/public_html/media/js/core.js index 072e145f..94203591 100644 --- a/public_html/media/js/core.js +++ b/public_html/media/js/core.js @@ -161,7 +161,7 @@ $(function() if (term != '') $.get(searchInput.attr('data-autocomplete-url') + '?json', {filter: term}, function(data) { - response($.map(data.tags, function(tag) { return { label: tag, value: tag }; })); + response($.map(data.tags, function(tag) { return { label: tag.name, value: tag.name }; })); }); }, focus: function() @@ -191,13 +191,18 @@ function getTagItOptions() function(request, response) { var term = request.term.toLowerCase(); - var results = $.grep(this.options.availableTags, function(a) + var tags = $.map(this.options.availableTags, function(a) + { + return a.name; + }); + var results = $.grep(tags, function(a) { if (term.length < 3) return a.toLowerCase().indexOf(term) == 0; else return a.toLowerCase().indexOf(term) != -1; }); + results = results.slice(0, 15); if (!this.options.allowDuplicates) results = this._subtractArray(results, this.assignedTags()); response(results); diff --git a/src/Bootstrap.php b/src/Bootstrap.php index 324dc1b3..15bd878d 100644 --- a/src/Bootstrap.php +++ b/src/Bootstrap.php @@ -21,7 +21,8 @@ class Bootstrap 'core.js', ]; - $this->context->layoutName = isset($_GET['json']) + $this->context->json = isset($_GET['json']); + $this->context->layoutName = $this->context->json ? 'layout-json' : 'layout-normal'; $this->context->transport = new StdClass; diff --git a/src/Controllers/CommentController.php b/src/Controllers/CommentController.php index ae0f4cde..efb20ddb 100644 --- a/src/Controllers/CommentController.php +++ b/src/Controllers/CommentController.php @@ -12,35 +12,19 @@ class CommentController $this->context->stylesheets []= 'comment-list.css'; $this->context->stylesheets []= 'comment-small.css'; $this->context->stylesheets []= 'paginator.css'; - $this->context->subTitle = 'comments'; if ($this->context->user->hasEnabledEndlessScrolling()) $this->context->scripts []= 'paginator-endless.js'; $page = intval($page); $commentsPerPage = intval($this->config->comments->commentsPerPage); + $this->context->subTitle = 'comments'; PrivilegesHelper::confirmWithException(Privilege::ListComments); - $buildDbQuery = function($dbQuery) - { - $dbQuery->from('comment'); - $dbQuery->orderBy('comment_date')->desc(); - }; - - $countDbQuery = R::$f->begin(); - $countDbQuery->select('COUNT(1)')->as('count'); - $buildDbQuery($countDbQuery); - $commentCount = intval($countDbQuery->get('row')['count']); + $commentCount = Model_Comment::getEntityCount(null); $pageCount = ceil($commentCount / $commentsPerPage); $page = max(1, min($pageCount, $page)); + $comments = Model_Comment::getEntities(null, $commentsPerPage, $page); - $searchDbQuery = R::$f->begin(); - $searchDbQuery->select('comment.*'); - $buildDbQuery($searchDbQuery); - $searchDbQuery->limit('?')->put($commentsPerPage); - $searchDbQuery->offset('?')->put(($page - 1) * $commentsPerPage); - - $comments = $searchDbQuery->get(); - $comments = R::convertToBeans('comment', $comments); R::preload($comments, ['commenter' => 'user', 'post', 'post.uploader' => 'user']); $this->context->postGroups = true; $this->context->transport->paginator = new StdClass; diff --git a/src/Controllers/PostController.php b/src/Controllers/PostController.php index 241e90d6..17f05998 100644 --- a/src/Controllers/PostController.php +++ b/src/Controllers/PostController.php @@ -82,79 +82,11 @@ class PostController $this->context->transport->searchQuery = $query; PrivilegesHelper::confirmWithException(Privilege::ListPosts); - $buildDbQuery = function($dbQuery, $query) - { - $dbQuery - ->addSql(', ') - ->open() - ->select('COUNT(1)') - ->from('comment') - ->where('comment.post_id = post.id') - ->close() - ->as('comment_count'); - - $dbQuery - ->addSql(', ') - ->open() - ->select('COUNT(1)') - ->from('favoritee') - ->where('favoritee.post_id = post.id') - ->close() - ->as('fav_count'); - - $dbQuery - ->addSql(', ') - ->open() - ->select('COUNT(1)') - ->from('post_tag') - ->where('post_tag.post_id = post.id') - ->close() - ->as('tag_count'); - - $dbQuery->from('post'); - - /* safety */ - $allowedSafety = array_filter(PostSafety::getAll(), function($safety) - { - return PrivilegesHelper::confirm(Privilege::ListPosts, PostSafety::toString($safety)) and - $this->context->user->hasEnabledSafety($safety); - }); - $dbQuery->where('safety')->in('(' . R::genSlots($allowedSafety) . ')'); - foreach ($allowedSafety as $s) - $dbQuery->put($s); - - - /* hidden */ - if (!PrivilegesHelper::confirm(Privilege::ListPosts, 'hidden')) - $dbQuery->andNot('hidden'); - - - /* query tokens */ - $tokens = array_filter(array_unique(explode(' ', $query)), function($x) { return $x != ''; }); - if (count($tokens) > $this->config->browsing->maxSearchTokens) - throw new SimpleException('Too many search tokens (maximum: ' . $this->config->browsing->maxSearchTokens . ')'); - - - /* tokens */ - $this->decorateSearchQuery($dbQuery, $tokens); - }; - - $countDbQuery = R::$f->begin(); - $countDbQuery->select('COUNT(1)')->as('count'); - $buildDbQuery($countDbQuery, $query); - $postCount = intval($countDbQuery->get('row')['count']); + $postCount = Model_Post::getEntityCount($query); $pageCount = ceil($postCount / $postsPerPage); $page = max(1, min($pageCount, $page)); + $posts = Model_Post::getEntities($query, $postsPerPage, $page); - $searchDbQuery = R::$f->begin(); - $searchDbQuery->select('post.*'); - $buildDbQuery($searchDbQuery, $query); - $searchDbQuery->limit('?')->put($postsPerPage); - $searchDbQuery->offset('?')->put(($page - 1) * $postsPerPage); - - $posts = $searchDbQuery->get(); - $posts = R::convertToBeans('post', $posts); - R::preload($posts, ['uploader' => 'user']); $this->context->transport->paginator = new StdClass; $this->context->transport->paginator->page = $page; $this->context->transport->paginator->pageCount = $pageCount; @@ -614,20 +546,6 @@ class PostController if ($fav->user->id == $this->context->user->id) $favorite = true; - $dbQuery = R::$f->begin(); - $dbQuery->select('tag.name, COUNT(1) AS count'); - $dbQuery->from('tag'); - $dbQuery->innerJoin('post_tag'); - $dbQuery->on('tag.id = post_tag.tag_id'); - $dbQuery->where('tag.id')->in('(' . R::genSlots($post->sharedTag) . ')'); - foreach ($post->sharedTag as $tag) - $dbQuery->put($tag->id); - $dbQuery->groupBy('tag.id'); - $rows = $dbQuery->get(); - $this->context->transport->tagDistribution = []; - foreach ($rows as $row) - $this->context->transport->tagDistribution[$row['name']] = $row['count']; - $this->context->stylesheets []= 'post-view.css'; $this->context->stylesheets []= 'comment-small.css'; $this->context->scripts []= 'post-view.js'; @@ -780,249 +698,4 @@ class PostController $this->context->transport->fileHash = 'post' . $post->file_hash; $this->context->transport->filePath = $path; } - - - - private function decorateSearchQuery($dbQuery, $tokens) - { - $orderColumn = 'post.id'; - $orderDir = 1; - $randomReset = true; - - foreach ($tokens as $token) - { - if ($token{0} == '-') - { - $andFunc = 'andNot'; - $token = substr($token, 1); - $neg = true; - } - else - { - $andFunc = 'and'; - $neg = false; - } - - $pos = strpos($token, ':'); - if ($pos === false) - { - $val = $token; - $dbQuery - ->$andFunc() - ->exists() - ->open() - ->select('1') - ->from('post_tag') - ->innerJoin('tag') - ->on('post_tag.tag_id = tag.id') - ->where('post_id = post.id') - ->and('LOWER(tag.name) = LOWER(?)')->put($val) - ->close(); - continue; - } - - $key = substr($token, 0, $pos); - $val = substr($token, $pos + 1); - - switch ($key) - { - case 'id': - $ids = preg_split('/[;,]/', $val); - $ids = array_map('intval', $ids); - $dbQuery->$andFunc('id')->in('(' . R::genSlots($ids) . ')'); - foreach ($ids as $id) - $dbQuery->put($id); - break; - - case 'idmin': - case 'idmax': - $operator = $key == 'idmin' ? '>=' : '<='; - $dbQuery->$andFunc('id ' . $operator . ' ?')->put(intval($val)); - break; - - case 'favmin': - case 'favmax': - $operator = $key == 'favmin' ? '>=' : '<='; - $dbQuery->$andFunc('fav_count ' . $operator . ' ?')->put(intval($val)); - break; - - case 'tagmin': - case 'tagmax': - $operator = $key == 'tagmin' ? '>=' : '<='; - $dbQuery->$andFunc('tag_count ' . $operator . ' ?')->put(intval($val)); - break; - - case 'commentmin': - case 'commentmax': - $operator = $key == 'commentmin' ? '>=' : '<='; - $dbQuery->$andFunc('comment_count ' . $operator . ' ?')->put(intval($val)); - break; - - case 'type': - 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 . '"'); - } - $dbQuery - ->$andFunc('type = ?') - ->put($type); - break; - - case 'date': - case 'datemin': - case 'datemax': - 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); - - if ($key == 'date') - { - $dbQuery - ->$andFunc('upload_date >= ?') - ->and('upload_date <= ?') - ->put($timeMin) - ->put($timeMax); - } - elseif ($key == 'datemin') - { - $dbQuery - ->$andFunc('upload_date >= ?') - ->put($timeMin); - } - elseif ($key == 'datemax') - { - $dbQuery - ->$andFunc('upload_date <= ?') - ->put($timeMax); - } - else - { - throw new Exception('Invalid key'); - } - - break; - - case 'fav': - case 'favs': - case 'favoritee': - case 'favoriter': - $dbQuery - ->$andFunc() - ->exists() - ->open() - ->select('1') - ->from('favoritee') - ->innerJoin('user') - ->on('favoritee.user_id = user.id') - ->where('post_id = post.id') - ->and('user.name = ?')->put($val) - ->close(); - break; - - case 'submit': - case 'upload': - case 'uploader': - case 'uploaded': - $dbQuery - ->$andFunc('uploader_id = ') - ->open() - ->select('user.id') - ->from('user') - ->where('name = ?')->put($val) - ->close(); - break; - - case 'order': - if (substr($val, -4) == 'desc') - { - $orderDir = 1; - $val = rtrim(substr($val, 0, -4), ','); - } - elseif (substr($val, -3) == 'asc') - { - $orderDir = -1; - $val = rtrim(substr($val, 0, -3), ','); - } - if ($val{0} == '-') - { - $orderDir *= -1; - $val = substr($val, 1); - } - if ($neg) - { - $orderDir *= -1; - } - - switch ($val) - { - case 'id': - $orderColumn = 'post.id'; - break; - case 'date': - $orderColumn = 'post.upload_date'; - break; - case 'comment': - case 'comments': - case 'commentcount': - $orderColumn = 'comment_count'; - break; - case 'fav': - case 'favs': - case 'favcount': - $orderColumn = 'fav_count'; - break; - case 'tag': - case 'tags': - case 'tagcount': - $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 . '"'); - } - break; - - default: - throw new SimpleException('Unknown key "' . $key . '"'); - } - } - - if ($randomReset) - unset($_SESSION['browsing-seed']); - - $dbQuery->orderBy($orderColumn); - if ($orderDir == 1) - $dbQuery->desc(); - else - $dbQuery->asc(); - } } diff --git a/src/Controllers/TagController.php b/src/Controllers/TagController.php index d22db520..269f5539 100644 --- a/src/Controllers/TagController.php +++ b/src/Controllers/TagController.php @@ -12,35 +12,13 @@ class TagController PrivilegesHelper::confirmWithException(Privilege::ListTags); $suppliedFilter = InputHelper::get('filter'); - $dbQuery = R::$f->begin(); - $dbQuery->select('tag.*, COUNT(1) AS count'); - $dbQuery->from('tag'); - $dbQuery->innerJoin('post_tag'); - $dbQuery->on('tag.id = post_tag.tag_id'); - if ($suppliedFilter !== null) - { - if (strlen($suppliedFilter) >= 3) - $suppliedFilter = '%' . $suppliedFilter; - $suppliedFilter .= '%'; - $dbQuery->where('LOWER(tag.name) LIKE LOWER(?)')->put($suppliedFilter); - } - $dbQuery->groupBy('tag.id'); - $dbQuery->orderBy('LOWER(tag.name)')->asc(); - if ($suppliedFilter !== null) - $dbQuery->limit(15); - $rows = $dbQuery->get(); - $tags = R::convertToBeans('tag', $rows); - - $tags = []; - $tagDistribution = []; - foreach ($rows as $row) - { - $tags []= strval($row['name']); - $tagDistribution[$row['name']] = intval($row['count']); - } - + $tags = Model_Tag::getEntities($suppliedFilter, null, null); $this->context->transport->tags = $tags; - $this->context->transport->tagDistribution = $tagDistribution; + + if ($this->context->json) + $this->context->transport->tags = array_values(array_map(function($tag) { + return ['name' => $tag->name, 'count' => $tag->getPostCount()]; + }, $this->context->transport->tags)); } /** diff --git a/src/Controllers/UserController.php b/src/Controllers/UserController.php index 693d18da..5b4fb29b 100644 --- a/src/Controllers/UserController.php +++ b/src/Controllers/UserController.php @@ -56,58 +56,21 @@ class UserController if ($this->context->user->hasEnabledEndlessScrolling()) $this->context->scripts []= 'paginator-endless.js'; - $page = intval($page); - $usersPerPage = intval($this->config->browsing->usersPerPage); - $this->context->subTitle = 'users'; - PrivilegesHelper::confirmWithException(Privilege::ListUsers); - if ($sortStyle == '' or $sortStyle == 'alpha') $sortStyle = 'alpha,asc'; if ($sortStyle == 'date') $sortStyle = 'date,asc'; - $buildDbQuery = function($dbQuery, $sortStyle) - { - $dbQuery->from('user'); + $page = intval($page); + $usersPerPage = intval($this->config->browsing->usersPerPage); + $this->context->subTitle = 'users'; + PrivilegesHelper::confirmWithException(Privilege::ListUsers); - switch ($sortStyle) - { - case 'alpha,asc': - $dbQuery->orderBy('name')->asc(); - break; - case 'alpha,desc': - $dbQuery->orderBy('name')->desc(); - break; - case 'date,asc': - $dbQuery->orderBy('join_date')->asc(); - break; - case 'date,desc': - $dbQuery->orderBy('join_date')->desc(); - break; - case 'pending': - $dbQuery->where('staff_confirmed IS NULL'); - $dbQuery->or('staff_confirmed = 0'); - break; - default: - throw new SimpleException('Unknown sort style'); - } - }; - - $countDbQuery = R::$f->begin(); - $countDbQuery->select('COUNT(1)')->as('count'); - $buildDbQuery($countDbQuery, $sortStyle); - $userCount = intval($countDbQuery->get('row')['count']); + $userCount = Model_User::getEntityCount($sortStyle); $pageCount = ceil($userCount / $usersPerPage); $page = max(1, min($pageCount, $page)); + $users = Model_User::getEntities($sortStyle, $usersPerPage, $page); - $searchDbQuery = R::$f->begin(); - $searchDbQuery->select('user.*'); - $buildDbQuery($searchDbQuery, $sortStyle); - $searchDbQuery->limit('?')->put($usersPerPage); - $searchDbQuery->offset('?')->put(($page - 1) * $usersPerPage); - - $users = $searchDbQuery->get(); - $users = R::convertToBeans('user', $users); $this->context->sortStyle = $sortStyle; $this->context->transport->paginator = new StdClass; $this->context->transport->paginator->page = $page; @@ -369,67 +332,19 @@ class UserController $this->context->scripts []= 'paginator-endless.js'; $this->context->subTitle = $name; - $buildDbQuery = function($dbQuery, $user, $tab) - { - $dbQuery->from('post'); + $query = ''; + if ($tab == 'uploads') + $query = 'submit:' . $user->name; + elseif ($tab == 'favs') + $query = 'fav:' . $user->name; + else + throw new SimpleException('Wrong tab'); - - /* safety */ - $allowedSafety = array_filter(PostSafety::getAll(), function($safety) - { - return PrivilegesHelper::confirm(Privilege::ListPosts, PostSafety::toString($safety)) and - $this->context->user->hasEnabledSafety($safety); - }); - $dbQuery->where('safety IN (' . R::genSlots($allowedSafety) . ')'); - foreach ($allowedSafety as $s) - $dbQuery->put($s); - - - /* hidden */ - if (!PrivilegesHelper::confirm(Privilege::ListPosts, 'hidden')) - $dbQuery->andNot('hidden'); - - - /* tab */ - switch ($tab) - { - case 'uploads': - $dbQuery - ->and('uploader_id = ?') - ->put($user->id); - break; - case 'favs': - $dbQuery - ->and() - ->exists() - ->open() - ->select('1') - ->from('favoritee') - ->where('post_id = post.id') - ->and('favoritee.user_id = ?') - ->put($user->id) - ->close(); - break; - } - }; - - $countDbQuery = R::$f->begin()->select('COUNT(*)')->as('count'); - $buildDbQuery($countDbQuery, $user, $tab); - $postCount = intval($countDbQuery->get('row')['count']); + $postCount = Model_Post::getEntityCount($query); $pageCount = ceil($postCount / $postsPerPage); $page = max(1, min($pageCount, $page)); + $posts = Model_Post::getEntities($query, $postsPerPage, $page); - $searchDbQuery = R::$f->begin()->select('*'); - $buildDbQuery($searchDbQuery, $user, $tab); - $searchDbQuery->orderBy('id DESC') - ->limit('?') - ->put($postsPerPage) - ->offset('?') - ->put(($page - 1) * $postsPerPage); - - $posts = $searchDbQuery->get(); - $posts = R::convertToBeans('post', $posts); - R::preload($posts, ['uploader' => 'user']); $this->context->transport->user = $user; $this->context->transport->tab = $tab; $this->context->transport->paginator = new StdClass; diff --git a/src/Models/AbstractModel.php b/src/Models/AbstractModel.php new file mode 100644 index 00000000..08f46eaa --- /dev/null +++ b/src/Models/AbstractModel.php @@ -0,0 +1,53 @@ +getNew()->begin(); + $dbQuery->select($table . '.*'); + $builder = static::getQueryBuilder(); + if ($builder) + $builder::build($dbQuery, $query); + else + $dbQuery->from($table); + if ($perPage !== null) + { + $dbQuery->limit('?')->put($perPage); + $dbQuery->offset('?')->put(($page - 1) * $perPage); + } + $rows = $dbQuery->get(); + return $rows; + } + + public static function getEntities($query, $perPage = null, $page = 1) + { + $table = static::getTableName(); + $rows = self::getEntitiesRows($query, $perPage, $page); + $entities = R::convertToBeans($table, $rows); + return $entities; + } + + public static function getEntityCount($query) + { + $table = static::getTableName(); + $dbQuery = R::$f->getNew()->begin(); + $dbQuery->select('COUNT(1)')->as('count'); + $builder = static::getQueryBuilder(); + if ($builder) + $builder::build($dbQuery, $query); + else + $dbQuery->from($table); + return intval($dbQuery->get('row')['count']); + } +} diff --git a/src/Models/AbstractQueryBuilder b/src/Models/AbstractQueryBuilder new file mode 100644 index 00000000..f736b114 --- /dev/null +++ b/src/Models/AbstractQueryBuilder @@ -0,0 +1,5 @@ +from('comment'); + $dbQuery->orderBy('id')->desc(); + } +} diff --git a/src/Models/Model_Post.php b/src/Models/Model_Post.php index ecdb564c..a99d740a 100644 --- a/src/Models/Model_Post.php +++ b/src/Models/Model_Post.php @@ -1,11 +1,11 @@ addSql(', ') + ->open() + ->select('COUNT(1)') + ->from($tableName) + ->where($tableName . '.post_id = post.id') + ->close() + ->as($shortName . '_count'); + } + + protected static function attachCommentCount($dbQuery) + { + self::attachTableCount($dbQuery, 'comment', 'comment'); + } + + protected static function attachFavCount($dbQuery) + { + self::attachTableCount($dbQuery, 'favoritee', 'fav'); + } + + protected static function attachTagCount($dbQuery) + { + self::attachTableCount($dbQuery, 'post_tag', 'tag'); + } + + protected static function filterUserSafety($dbQuery) + { + $context = \Chibi\Registry::getContext(); + $allowedSafety = array_filter(PostSafety::getAll(), function($safety) use ($context) + { + return PrivilegesHelper::confirm(Privilege::ListPosts, PostSafety::toString($safety)) and + $context->user->hasEnabledSafety($safety); + }); + $dbQuery->addSql('safety')->in('(' . R::genSlots($allowedSafety) . ')'); + foreach ($allowedSafety as $s) + $dbQuery->put($s); + } + + protected static function filterUserHidden($dbQuery) + { + if (!PrivilegesHelper::confirm(Privilege::ListPosts, 'hidden')) + $dbQuery->not()->addSql('hidden'); + else + $dbQuery->addSql('1'); + } + + protected static function filterChain($dbQuery) + { + if (isset($dbQuery->__chained)) + $dbQuery->and(); + else + $dbQuery->where(); + $dbQuery->__chained = true; + } + + protected static function filterNegate($dbQuery) + { + $dbQuery->not(); + } + + protected static function filterTag($dbQuery, $val) + { + $dbQuery + ->exists() + ->open() + ->select('1') + ->from('post_tag') + ->innerJoin('tag') + ->on('post_tag.tag_id = tag.id') + ->where('post_id = post.id') + ->and('LOWER(tag.name) = LOWER(?)')->put($val) + ->close(); + } + + protected static function filterTokenId($dbQuery, $val) + { + $ids = preg_split('/[;,]/', $val); + $ids = array_map('intval', $ids); + $dbQuery->addSql('id')->in('(' . R::genSlots($ids) . ')'); + foreach ($ids as $id) + $dbQuery->put($id); + } + + protected static function filterTokenIdMin($dbQuery, $val) + { + $dbQuery->addSql('id >= ?')->put(intval($val)); + } + + protected static function filterTokenIdMax($dbQuery, $val) + { + $dbQuery->addSql('id <= ?')->put(intval($val)); + } + + protected static function filterTokenTagMin($dbQuery, $val) + { + $dbQuery->addSql('tag_count >= ?')->put(intval($val)); + } + + protected static function filterTokenTagMax($dbQuery, $val) + { + $dbQuery->addSql('tag_count <= ?')->put(intval($val)); + } + + protected static function filterTokenFavMin($dbQuery, $val) + { + $dbQuery->addSql('fav_count >= ?')->put(intval($val)); + } + + protected static function filterTokenFavMax($dbQuery, $val) + { + $dbQuery->addSql('fav_count <= ?')->put(intval($val)); + } + + protected static function filterTokenCommentMin($dbQuery, $val) + { + $dbQuery->addSql('comment_count >= ?')->put(intval($val)); + } + + protected static function filterTokenCommentMax($dbQuery, $val) + { + $dbQuery->addSql('comment_count <= ?')->put(intval($val)); + } + + protected static function filterTokenType($dbQuery, $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 . '"'); + } + $dbQuery->addSql('type = ?')->put($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($dbQuery, $val) + { + list ($timeMin, $timeMax) = self::__filterTokenDateParser($val); + $dbQuery + ->addSql('upload_date >= ?')->and('upload_date <= ?') + ->put($timeMin) + ->put($timeMax); + } + + protected static function filterTokenDateMin($dbQuery, $val) + { + list ($timeMin, $timeMax) = self::__filterTokenDateParser($val); + $dbQuery->addSql('upload_date >= ?')->put($timeMin); + } + + protected static function filterTokenDateMax($dbQuery, $val) + { + list ($timeMin, $timeMax) = self::__filterTokenDateParser($val); + $dbQuery->addSql('upload_date <= ?')->put($timeMax); + } + + protected static function filterTokenFav($dbQuery, $val) + { + $dbQuery + ->exists() + ->open() + ->select('1') + ->from('favoritee') + ->innerJoin('user') + ->on('favoritee.user_id = user.id') + ->where('post_id = post.id') + ->and('user.name = ?')->put($val) + ->close(); + } + + protected static function filterTokenFavs($dbQuery, $val) + { + return self::filterTokenFav($dbQuery, $val); + } + + protected static function filterTokenFavitee($dbQuery, $val) + { + return self::filterTokenFav($dbQuery, $val); + } + + protected static function filterTokenFaviter($dbQuery, $val) + { + return self::filterTokenFav($dbQuery, $val); + } + + protected static function filterTokenSubmit($dbQuery, $val) + { + $dbQuery + ->addSql('uploader_id = ') + ->open() + ->select('user.id') + ->from('user') + ->where('name = ?')->put($val) + ->close(); + } + + protected static function filterTokenUploader($dbQuery, $val) + { + return self::filterTokenSubmit($dbQuery, $val); + } + + protected static function filterTokenUpload($dbQuery, $val) + { + return self::filterTokenSubmit($dbQuery, $val); + } + + protected static function filterTokenUploaded($dbQuery, $val) + { + return self::filterTokenSubmit($dbQuery, $val); + } + + + + protected static function order($dbQuery, $val) + { + $randomReset = true; + + $orderDir = 1; + if (substr($val, -4) == 'desc') + { + $orderDir = 1; + $val = rtrim(substr($val, 0, -4), ','); + } + elseif (substr($val, -3) == 'asc') + { + $orderDir = -1; + $val = rtrim(substr($val, 0, -3), ','); + } + if ($val{0} == '-') + { + $orderDir *= -1; + $val = substr($val, 1); + } + + switch ($val) + { + case 'id': + $orderColumn = 'post.id'; + break; + case 'date': + $orderColumn = 'post.upload_date'; + break; + case 'comment': + case 'comments': + case 'commentcount': + $orderColumn = 'comment_count'; + break; + case 'fav': + case 'favs': + case 'favcount': + $orderColumn = 'fav_count'; + break; + case 'tag': + case 'tags': + case 'tagcount': + $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) + unset($_SESSION['browsing-seed']); + + $dbQuery->orderBy($orderColumn); + if ($orderDir == 1) + $dbQuery->desc(); + else + $dbQuery->asc(); + } + + + + public static function build($dbQuery, $query) + { + $config = \Chibi\Registry::getConfig(); + $context = \Chibi\Registry::getContext(); + + self::attachCommentCount($dbQuery); + self::attachFavCount($dbQuery); + self::attachTagCount($dbQuery); + + $dbQuery->from('post'); + + self::filterChain($dbQuery); + self::filterUserSafety($dbQuery); + self::filterChain($dbQuery); + self::filterUserHidden($dbQuery); + + /* query tokens */ + $tokens = array_filter(array_unique(explode(' ', $query)), function($x) { return $x != ''; }); + if (count($tokens) > $config->browsing->maxSearchTokens) + throw new SimpleException('Too many search tokens (maximum: ' . $config->browsing->maxSearchTokens . ')'); + + $orderToken = 'id'; + foreach ($tokens as $token) + { + if ($token{0} == '-') + { + $token = substr($token, 1); + $neg = true; + } + else + { + $neg = false; + } + + $pos = strpos($token, ':'); + if ($pos === false) + { + self::filterChain($dbQuery); + if ($neg) + self::filterNegate($dbQuery); + self::filterTag($dbQuery, $token); + continue; + } + + $key = substr($token, 0, $pos); + $val = substr($token, $pos + 1); + + $methodName = 'filterToken' . TextHelper::kebabCaseToCamelCase($key); + if (method_exists(__CLASS__, $methodName)) + { + self::filterChain($dbQuery); + if ($neg) + self::filterNegate($dbQuery); + self::$methodName($dbQuery, $val); + } + + elseif ($key == 'order') + { + if ($neg) + $orderToken = $val; + else + $orderToken = '-' . $val; + } + + else + { + throw new SimpleException('Unknown key "' . $key . '"'); + } + } + + self::order($dbQuery, $orderToken); + } +} diff --git a/src/Models/Model_Tag.php b/src/Models/Model_Tag.php index 03e44831..d16b69c3 100644 --- a/src/Models/Model_Tag.php +++ b/src/Models/Model_Tag.php @@ -1,5 +1,5 @@ bean->getMeta('post_count')) + return $this->bean->getMeta('post_count'); + return $this->bean->countShared('post'); + } + + public static function validateTags($tags) { $tags = trim($tags); @@ -65,4 +73,31 @@ class Model_Tag extends RedBean_SimpleModel return $tags; } + + public static function getTableName() + { + return 'tag'; + } + + public static function getQueryBuilder() + { + return 'Model_Tag_Querybuilder'; + } + + public static function getEntities($query, $perPage = null, $page = 1) + { + $table = static::getTableName(); + $rows = self::getEntitiesRows($query, $perPage, $page); + $entities = R::convertToBeans($table, $rows); + + $rowMap = []; + foreach ($rows as &$row) + $rowMap[$row['id']] = $row; + unset ($row); + + foreach ($entities as $entity) + $entity->setMeta('post_count', $rowMap[$entity->id]['count']); + + return $entities; + } } diff --git a/src/Models/Model_Tag_QueryBuilder.php b/src/Models/Model_Tag_QueryBuilder.php new file mode 100644 index 00000000..8d311272 --- /dev/null +++ b/src/Models/Model_Tag_QueryBuilder.php @@ -0,0 +1,24 @@ +addSql(', COUNT(post_tag.post_id)')->as('count'); + $dbQuery->from('tag'); + $dbQuery->innerJoin('post_tag'); + $dbQuery->on('tag.id = post_tag.tag_id'); + if ($query !== null) + { + $limitQuery = true; + if (strlen($query) >= 3) + $query = '%' . $query; + $query .= '%'; + $dbQuery->where('LOWER(tag.name) LIKE LOWER(?)')->put($query); + } + $dbQuery->groupBy('tag.id'); + $dbQuery->orderBy('LOWER(tag.name)')->asc(); + if ($limitQuery) + $dbQuery->limit(15); + } +} diff --git a/src/Models/Model_User.php b/src/Models/Model_User.php index 03dcc5e2..19d190da 100644 --- a/src/Models/Model_User.php +++ b/src/Models/Model_User.php @@ -1,9 +1,9 @@ email_confirmed and \Chibi\Registry::getConfig()->registration->needEmailForRegistering) @@ -159,4 +159,13 @@ class Model_User extends RedBean_SimpleModel return sha1($salt1 . $salt2 . $pass); } + public static function getTableName() + { + return 'user'; + } + + public static function getQueryBuilder() + { + return 'Model_User_QueryBuilder'; + } } diff --git a/src/Models/Model_User_QueryBuilder.php b/src/Models/Model_User_QueryBuilder.php new file mode 100644 index 00000000..c1c44bea --- /dev/null +++ b/src/Models/Model_User_QueryBuilder.php @@ -0,0 +1,31 @@ +from('user'); + + switch ($sortStyle) + { + case 'alpha,asc': + $dbQuery->orderBy('name')->asc(); + break; + case 'alpha,desc': + $dbQuery->orderBy('name')->desc(); + break; + case 'date,asc': + $dbQuery->orderBy('join_date')->asc(); + break; + case 'date,desc': + $dbQuery->orderBy('join_date')->desc(); + break; + case 'pending': + $dbQuery->where('staff_confirmed IS NULL'); + $dbQuery->or('staff_confirmed = 0'); + break; + default: + throw new SimpleException('Unknown sort style'); + } + } +} diff --git a/src/Views/post-view.phtml b/src/Views/post-view.phtml index b4620215..ba7f4fdf 100644 --- a/src/Views/post-view.phtml +++ b/src/Views/post-view.phtml @@ -34,7 +34,7 @@ name ?> - context->transport->tagDistribution[$tag->name]) ?> + getPostCount()) ?> diff --git a/src/Views/tag-list.phtml b/src/Views/tag-list.phtml index e2c59358..97549116 100644 --- a/src/Views/tag-list.phtml +++ b/src/Views/tag-list.phtml @@ -1,10 +1,10 @@ -context->transport->tagDistribution) ?> +getPostCount(); }, $this->context->transport->tags)); ?>