From b97726f6ff3be028b0f30c89376d76c82e34e7fe Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Mon, 4 Aug 2014 22:15:10 +0200 Subject: [PATCH] Changed behavior of post list tabs Before: each one linked to separate page that contained "static" search. After: each one links to generic search that is aware of current search. Example: if you searched for "snow" and clicked "upvoted", you would see all upvoted posts ever regardless of what you just searched for. After this change, you will see upvoted posts that have tag "snow". Now, if you go back to "all posts", you will see again all posts tagged with "snow" with or without the upvotes. Similarly if you click favorites, favmin:1 will be appended to your search. In order to totally reset your search, click "browse". Additionally, typing favmin:1 and scoremin:1 manually now selects proper tab. Previously tabs were marked only if you clicked the tab. Unfortunately, all of this had to happen at expense of URLs like /upvoted and /random - now everything is represented with plain /posts/. --- src/Controllers/PostController.php | 15 ---- .../SearchParsers/AbstractSearchParser.php | 34 +++++++- .../SearchServices/AbstractSearchService.php | 78 ++++++++++-------- .../SearchServices/PostSearchService.php | 2 +- src/Router.php | 7 -- src/Views/post/post-list-wrapper.phtml | 80 ++++++++++++------- 6 files changed, 128 insertions(+), 88 deletions(-) diff --git a/src/Controllers/PostController.php b/src/Controllers/PostController.php index 82c0ccc2..a6a48679 100644 --- a/src/Controllers/PostController.php +++ b/src/Controllers/PostController.php @@ -70,21 +70,6 @@ class PostController extends AbstractController $this->redirect($url); } - public function favoritesView($page = 1) - { - $this->listView('favmin:1', $page, 'favorites'); - } - - public function upvotedView($page = 1) - { - $this->listView('scoremin:1', $page, 'upvoted'); - } - - public function randomView($page = 1) - { - $this->listView('order:random', $page, 'random'); - } - public function toggleTagAction($identifier, $tag, $enable) { Access::assert(new Privilege( diff --git a/src/Models/SearchParsers/AbstractSearchParser.php b/src/Models/SearchParsers/AbstractSearchParser.php index da0daff7..316c9ec7 100644 --- a/src/Models/SearchParsers/AbstractSearchParser.php +++ b/src/Models/SearchParsers/AbstractSearchParser.php @@ -5,11 +5,41 @@ abstract class AbstractSearchParser { protected $statement; - public function decorate($statement, $filterString) + public function disassembleTokens($searchString) + { + return preg_split('/\s+/', $searchString); + } + + public function assembleTokens($tokens) + { + return implode(' ', $tokens); + } + + public function addTokenToSearchString($searchString, $token) + { + $tokensToInclude = is_array($token) + ? $token + : [$token]; + $tokens = $this->disassembleTokens($searchString); + $newTokens = array_filter(array_unique(array_merge($tokens, $tokensToInclude))); + return $this->assembleTokens($newTokens); + } + + public function removeTokenFromSearchString($searchString, $token) + { + $tokensToExclude = is_array($token) + ? $token + : [$token]; + $tokens = $this->disassembleTokens($searchString); + $newTokens = array_diff($tokens, $tokensToExclude); + return $this->assembleTokens($newTokens); + } + + public function decorate($statement, $searchString) { $this->statement = $statement; - $tokens = preg_split('/\s+/', $filterString); + $tokens = $this->disassembleTokens($searchString); $tokens = array_filter($tokens); $tokens = array_unique($tokens); $this->processSetup($tokens); diff --git a/src/Models/SearchServices/AbstractSearchService.php b/src/Models/SearchServices/AbstractSearchService.php index 9f0aeec3..fc417464 100644 --- a/src/Models/SearchServices/AbstractSearchService.php +++ b/src/Models/SearchServices/AbstractSearchService.php @@ -3,38 +3,7 @@ use \Chibi\Sql as Sql; abstract class AbstractSearchService { - protected static function getModelClassName() - { - $searchServiceClassName = get_called_class(); - $modelClassName = str_replace('SearchService', 'Model', $searchServiceClassName); - return $modelClassName; - } - - protected static function getParserClassName() - { - $searchServiceClassName = get_called_class(); - $parserClassName = str_replace('SearchService', 'SearchParser', $searchServiceClassName); - return $parserClassName; - } - - protected static function decorateParser($stmt, $searchQuery) - { - $parserClassName = self::getParserClassName(); - (new $parserClassName)->decorate($stmt, $searchQuery); - } - - protected static function decorateCustom($stmt) - { - } - - protected static function decoratePager($stmt, $perPage, $page) - { - if ($perPage === null) - return; - $stmt->setLimit( - new Sql\Binding($perPage), - new Sql\Binding(($page - 1) * $perPage)); - } + protected static $parser; public static function getEntities($searchQuery, $perPage = null, $page = 1) { @@ -44,7 +13,7 @@ abstract class AbstractSearchService $stmt = Sql\Statements::select(); $stmt->setColumn($table . '.*'); $stmt->setTable($table); - static::decorateParser($stmt, $searchQuery); + static::decorateFromParser($stmt, $searchQuery); static::decorateCustom($stmt); static::decoratePager($stmt, $perPage, $page); @@ -60,7 +29,7 @@ abstract class AbstractSearchService $innerStmt = Sql\Statements::select(); $innerStmt->setTable($table); - static::decorateParser($innerStmt, $searchQuery); + static::decorateFromParser($innerStmt, $searchQuery); static::decorateCustom($innerStmt); $innerStmt->resetOrderBy(); @@ -70,4 +39,45 @@ abstract class AbstractSearchService return Core::getDatabase()->fetchOne($stmt)['count']; } + + public static function getParser() + { + $parserClassName = self::getParserClassName(); + if (self::$parser == null) + self::$parser = new $parserClassName(); + return self::$parser; + } + + protected static function getModelClassName() + { + $searchServiceClassName = get_called_class(); + $modelClassName = str_replace('SearchService', 'Model', $searchServiceClassName); + return $modelClassName; + } + + protected static function decorateFromParser($stmt, $searchQuery) + { + self::getParser()->decorate($stmt, $searchQuery); + } + + protected static function decorateCustom($stmt) + { + } + + protected static function decoratePager($stmt, $perPage, $page) + { + if ($perPage === null) + return; + $stmt->setLimit( + new Sql\Binding($perPage), + new Sql\Binding(($page - 1) * $perPage)); + } + + private static function getParserClassName() + { + $searchServiceClassName = get_called_class(); + $parserClassName = str_replace('SearchService', 'SearchParser', $searchServiceClassName); + return $parserClassName; + } + } diff --git a/src/Models/SearchServices/PostSearchService.php b/src/Models/SearchServices/PostSearchService.php index cc891cab..fa7d27d4 100644 --- a/src/Models/SearchServices/PostSearchService.php +++ b/src/Models/SearchServices/PostSearchService.php @@ -20,7 +20,7 @@ class PostSearchService extends AbstractSearchService $innerStmt = Sql\Statements::select(); $innerStmt->setColumn('post.id'); $innerStmt->setTable('post'); - self::decorateParser($innerStmt, $searchQuery); + self::decorateFromParser($innerStmt, $searchQuery); $stmt = Sql\Statements::insert(); $stmt->setTable('post_search'); $stmt->setSource(['post_id'], $innerStmt); diff --git a/src/Router.php b/src/Router.php index 8ef719b8..3d3e37f2 100644 --- a/src/Router.php +++ b/src/Router.php @@ -63,13 +63,6 @@ class Router extends \Chibi\Routing\Router $this->register(['PostController', 'listView'], 'GET', '/{source}/{query}/{additionalInfo}/{page}', $postValidation); $this->register(['PostController', 'listRedirectAction'], 'POST', '/{source}-redirect', $postValidation); - $this->register(['PostController', 'randomView'], 'GET', '/random', $postValidation); - $this->register(['PostController', 'randomView'], 'GET', '/random/{page}', $postValidation); - $this->register(['PostController', 'favoritesView'], 'GET', '/favorites', $postValidation); - $this->register(['PostController', 'favoritesView'], 'GET', '/favorites/{page}', $postValidation); - $this->register(['PostController', 'upvotedView'], 'GET', '/upvoted', $postValidation); - $this->register(['PostController', 'upvotedView'], 'GET', '/upvoted/{page}', $postValidation); - $this->register(['PostController', 'genericView'], 'GET', '/post/{identifier}', $postValidation); $this->register(['PostController', 'fileView'], 'GET', '/post/{name}/retrieve', $postValidation); $this->register(['PostController', 'thumbnailView'], 'GET', '/post/{name}/thumb', $postValidation); diff --git a/src/Views/post/post-list-wrapper.phtml b/src/Views/post/post-list-wrapper.phtml index 61efa072..cfadbc52 100644 --- a/src/Views/post/post-list-wrapper.phtml +++ b/src/Views/post/post-list-wrapper.phtml @@ -1,59 +1,81 @@ assets->setSubTitle('posts'); +$searchQuery = isset($this->context->transport->searchQuery) + ? htmlspecialchars($this->context->transport->searchQuery) + : ''; + +$parser = PostSearchService::getParser(); +$newSearchQuery = PostSearchService::getParser()->removeTokenFromSearchString( + $searchQuery, + ['favmin:1', 'scoremin:1', 'order:random']); $tabs = []; $activeTab = 0; -if (Access::check(new Privilege(Privilege::ListPosts))) - $tabs []= ['All posts', Core::getRouter()->linkTo(['PostController', 'listView'])]; - if (Access::check(new Privilege(Privilege::ListPosts))) { - $tabs []= ['Random', Core::getRouter()->linkTo(['PostController', 'randomView'])]; - if ($this->context->source == 'random') - $activeTab = count($tabs) - 1; + $tabs []= [ + 'title' => 'All posts', + 'active' => function() { return true; }, + 'link' => Core::getRouter()->linkTo( + ['PostController', 'listView'], + !trim($newSearchQuery) ? [] : ['query' => $newSearchQuery])]; - $tabs []= ['Favorites', Core::getRouter()->linkTo(['PostController', 'favoritesView'])]; - if ($this->context->source == 'favorites') - $activeTab = count($tabs) - 1; + $tabs []= [ + 'title' => 'Random', + 'active' => function() use ($searchQuery) { return strpos($searchQuery, 'order:random') !== false; }, + 'link' => Core::getRouter()->linkTo( + ['PostController', 'listView'], + ['query' => $parser->addTokenToSearchString($newSearchQuery, 'order:random')])]; - $tabs []= ['Upvoted', Core::getRouter()->linkTo(['PostController', 'upvotedView'])]; - if ($this->context->source == 'upvoted') - $activeTab = count($tabs) - 1; + $tabs []= [ + 'title' => 'Favorites', + 'active' => function() use ($searchQuery) { return strpos($searchQuery, 'favmin:1') !== false; }, + 'link' => Core::getRouter()->linkTo( + ['PostController', 'listView'], + ['query' => $parser->addTokenToSearchString($newSearchQuery, 'favmin:1')])]; + + $tabs []= [ + 'title' => 'Upvoted', + 'active' => function() use ($searchQuery) { return strpos($searchQuery, 'scoremin:1') !== false; }, + 'link' => Core::getRouter()->linkTo( + ['PostController', 'listView'], + ['query' => $parser->addTokenToSearchString($newSearchQuery, 'scoremin:1')])]; } if (Access::check(new Privilege(Privilege::MassTag))) { - $tabs []= ['Mass tag', Core::getRouter()->linkTo(['PostController', 'listView'], [ - 'source' => 'mass-tag', - 'query' => isset($this->context->transport->searchQuery) - ? htmlspecialchars($this->context->transport->searchQuery) - : '', - 'page' => isset($this->context->transport->paginator) - ? $this->context->transport->paginator->page - : 1])]; - - if ($this->context->source == 'mass-tag') - $activeTab = count($tabs) - 1; + $tabs []= [ + 'title' => 'Mass tag', + 'active' => function() { return $this->context->source == 'mass-tag'; }, + 'link' => Core::getRouter()->linkTo(['PostController', 'listView'], [ + 'source' => 'mass-tag', + 'query' => $searchQuery, + 'page' => isset($this->context->transport->paginator) + ? $this->context->transport->paginator->page + : 1])]; } +$activeTab = null; +foreach ($tabs as $key => $tab) + if ($tab['active']()) + $activeTab = $key; ?>